3

I am designing an API interface in a 2-tier architecture. It takes a string parameter fieldName from URL and returns a JSON string. fieldName refers to a field in my database table. You can think of its signature as:

def controller(fieldName: String): String

In the controller, I would like to call a method in my data access layer to perform the following query:

SELECT fieldName, SUM(salary) FROM Employee GROUP BY fieldName

because the type of the field varies, the type of the query result will be different. This method is parametrized by a generic type parameter T which corresponds to the type of the field with name fieldName.

def getTotalSalaryByField[T](fieldName: String): Map[T, Long]
  • if fieldName is "age", T should be Int.
  • if fieldName is "name", T should be String. and so on.

given a particular fieldName at runtime, how do I call this method giving it the correct type?

I don't want to write a lot of if-else or pattern matching statements to select the type. It would look like this:

fieldName match {
    case "age" => serializeToJson(getTotalSalaryByField[Int]("age"))
    case "name" => serializeToJson(getTotalSalaryByField[String]("name"))
    ...
    // 100 more for 100 more fields
}

This is ugly. If I were to write this in Python, it will take only one line:

json.dumps(getTotalSalaryByField(fieldName))

Is Scala somehow not suitable for rest backend programming? because this seems to be a common pattern people will encounter, and static typing gets in the way. I would like to see some suggestions as to how I should approach the whole problem in scala-ish way, even if it means remodeling, rewriting the DAL and controllers.

EDIT: @drexin, the actual signature of the DAL method is

def myDAOMethod[T](tf: Option[TimeFilter], cf: CrossFilter)
    (implicit attr: XFAttribute[T]): Map[T, Long]

T is the type of fieldName. Long is the type of y. As you can see, I need to select a type to be T based on fieldName from url parameter.

EDIT: added some code and made the usecase clear

10
  • 3
    What would you like Scala to do for you? Infer from different text types? Is there any other way to do it but pattern matching? Commented Aug 26, 2014 at 8:16
  • Could you please provide the signature of the method you want to call? Commented Aug 26, 2014 at 8:20
  • 1
    May be sqltyped project is what you are really after, This uses scala macros to provide the correct type for the column. Commented Aug 26, 2014 at 8:29
  • @goral, "Is there any other way to do it but pattern matching?" that's my question. Commented Aug 26, 2014 at 8:54
  • @drexin, question edited. Commented Aug 26, 2014 at 8:54

4 Answers 4

2
+50

There is a difference between knowing the type of a variable at compile time and at runtime.

If the fieldName is not known at compile time (i.e. it's a parameter), and if the type of the column varies by fieldName, then you are not going to be able to specify the return type of the method at compile time.

You will need to use a DAO method that returns AnyRef, rather than one which returns T for a compile-time-specified type T.

Old answer:

The database access library can return the values without needing to know their type, and your code needs to do the same.

You are looking to use a DAO method which takes a type param T:

def myDAOMethod[T](tf: Option[TimeFilter], cf: CrossFilter)
    (implicit attr: XFAttribute[T]): Map[T, Long]

... but as you state, you don't know the type T in advance, so this method is inapplicable. (T is used to convert the database column data into a String or an Int).

Your DAO should offer an untyped version of this method, something more like:

def doSelect(tf: Option[TimeFilter], cf: CrossFilter): Map[AnyRef, Long]

What database access library are you using?

Sign up to request clarification or add additional context in comments.

1 Comment

Gridgain. I convert from Any to a specific type in my DAO method after the results are returned.
2

If you are using Play framework then I would recommend you to use Play JSON APIs.

https://www.playframework.com/documentation/2.1.1/ScalaJson

Instead of

def getTotalSalaryByField[T](fieldName: String): Map[T, Long] = ???

you can write

def getTotalSalaryByField(fieldName: String): Map[JsValue, Long] = ???

since all types like JsNumber, JsString, JsNull are inherit from the generic JSON trait, JsValue.

wrap your queryResult with Json.toJson()

OR

def getTotalSalaryByField(fieldName: String): JsObject = ???

Comments

1

I think you have three options here:

Third option is probably what you are looking for, but it bases on experimental scala features (i.e. macros). What I imagine it would be doing is connecting to your database during the compilation phase and inspecting the schema. If you are generating schema using some separate sql file(s) then it should be even easier. Then macro will generate all boilerplate with proper types hard-coded.

Probably you won't find working example for exactly what you need but there is one for RFC files which you can use as inspiration: https://github.com/travisbrown/type-provider-examples

Comments

0

I'm not 100% sure I understand your question, so apologies if I'm answering something else entirely.

You need Scala to be able to guess the type of your field based on the expected return type. That is, in the following code:

val result : Map[String, Long] = myDAOMethod(tf, cf)

You expect Scala to correctly infer that since you want a Map[String, Long], your x variable is of type T.

It seems to me that you already have everything you need for that. I do not know what XFAttribute[T] is, but I suspect it allows you to transform entries in a result set to instances of type T.

What's more, you've already declared it as a implicit parameter.

Provided you have an implicit XFAttribute[String] in scope, then, the previous code should compile, run, and be type-safe.

As a small improvement, I'd change the signature of your method to use context bounds, but that's primarily a matter of taste:

// Declare the implicit "parsers"
implicit val IntAttribute: XFAttribute[Int] = ???
implicit val StringAttribute: XFAttribute[String] = ???

// Empty implementation, fill it with your actual code.
def myDAOMethod[T: XFAttribute](tf: Option[TimeFilter], cf: CrossFilter): Map[T, Long] = ???

// Scala will look for an implicit XFAttribute[Int] in scope, and find IntAttribute.
val ages: Map[Int, Long] = myDAOMethod(tf, cf)

// Scala will look for an implicit XFAttribute[String] in scope, and find StringAttribute.
val names: Map[String, Long] = myDAOMethod(tf, cf)

I'm not sure whether your question implies that you'd also like to strongly tie the String "age" to the type Int. That's also possible, of course, but it's another answer entirely and I don't want to pollute this question with unnecessary rambling.

3 Comments

Thanks for taking the time to write this. No, the question is not about guessing T from the return type, it is about getting T from the value of x, calling the method with T, doing so without writing a long list of if-else statements.
Do you mean to say that you know neither the field's name nor its type when you call myDAOMethod? Would you mind updating your question with a use case, possibly some code showing how you expect your method to be used, ... so that the constraints you're working with are clearer? It might be just me, but I'm finding it rather hard to understand precisely what it is you're trying to do.
question edited. I know the field's name and I know the type.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.