Scala - calling a method with a generic type parameter given a string value that determines the correct type

I am designing an API in a two-tier architecture. It takes stringName from the URL and returns a JSON string. fieldName refers to a field in my database table. You can represent your signature as:

def controller(fieldName: String): String

      

In the controller, I would like to call a method on my data access layer to make the following request:

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

      

since the type of the field changes, the type of the query result will be different. This method is parameterized by a generic type parameter T that matches the type of the field named nameName.

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

      

  • If fieldName is "age", T must be Int.
  • If fieldName is "name", T must be a string. etc.

given a specific field name at runtime, how can I call this method with the correct type?

I don't want to write many if-else or pattern statements to select the type. It will look like this:

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

      

It's not beautiful. If I were to write this in Python, it only takes one line:

json.dumps(getTotalSalaryByField(fieldName))

      

Is Scala somehow unsuitable for resting backend programming? because this seems to be the general nature that people run into and static typing gets in the way. I would like to offer some suggestions as to how I should approach the whole problem in a scala -ish way, even if that means remodeling, rewriting DALs and controllers.

CHANGE : @drexin, the actual signature DAL method

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

      

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

EDIT : added some code and made usecase clear

+3


source to share


4 answers


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

If fieldName

not known at compile time (i.e. it is a parameter), and if the column type changes to fieldName

, then you will not be able to specify the return type of the method at compile time.

You will need to use the DAO method that returns AnyRef

, not the one that returns T

for the compile time type T

.

Old answer:

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

You want to use a DAO method that takes a param type 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 beforehand T

, so this method is not applicable. ( T

used to convert database column data to String or Int).

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

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

      

Which database access library are you using?

+2


source


If you are using the Play Framework I would recommend that you use the Play JSON API.

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

Instead

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

      

You can write



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

      

since all types, such as JsNumber

, JsString

, JsNull

inherited from common feature JSON, JsValue

.

wrap your RequestResult with Json.toJson()

OR

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

      

+2


source


I think you have three options:

The third option is probably what you are looking for, but it is based on experimental scala features (i.e. macros). I assume this will involve connecting to your database at compile time and checking the schema. If you are creating a schema using some separate sql file (s) then it should be even easier. The macro will then generate the entire template with the correct types of hardcoding.

You probably won't find a working example for what you need, but there is one for RFC files that you can use as inspiration: https://github.com/travisbrown/type-provider-examples

+1


source


I'm not 100% sure, I understand your question, so I apologize if I fully answer something else.

You need Scala 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, as you want Map[String, Long]

, your variable x

is of type T

.

It seems to me that you already have everything you need. I don't know what it is XFAttribute[T]

, but I suspect it allows you to convert the records in the result set to instances of the type T

.

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

If you have an implicit scope XFAttribute[String]

, then the previous code should compile, run, and be type safe.

As a small improvement, I would change the signature of your method to use context bounds, but this is 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 if your question implies that you would also like to bind a string "age"

to a type Int

. This is also possible, of course, but this is another answer, and I don't want to pollute this question with unnecessary incoherent.

0


source







All Articles