Hello, newbie question here, somewhat similar to this thread and also this one, but for Scala 2.
I have a data record coming from a Java library, in the form of a List[Object]
. I know in advance the size and the sequence of types in the list, so it would make sense to create a case class to hold the data, in order to facilitate field access in the downstream logic.
I’d like to create a generic utility function, that takes in two parameters:
- a reference to the case class’ generated companion object (aka its factory function)
- the list of values to populate my instance with
The function should return an instance of the case class, or throw an exception in case the List elements don’t match the case class types.
I’ve found a way to accomplish this by calling the .curried
method exposed by the generated companion object of the case class, using recursion to pierce through the chain of functions, while passing in my list values along the way:
def rec[T,R](cur: T => _, vs: List[_]): R = {
cur(vs.head.asInstanceOf[T]) match {
case f: Function1[_, _] => rec(f, vs.tail)
case o => o.asInstanceOf[R] // reached the end of the function chain
}
}
def fill[T,R](cc: Function1[T,R], values: List[_]): R = rec[T,R](cc, values) // no recursion in this case
def fill[T,R](cc: Function2[T,_,R], values: List[_]): R = rec[T,R](cc.curried, values)
def fill[T,R](cc: Function3[T,_,_,R], values: List[_]): R = rec[T,R](cc.curried, values)
...
def fill[T,R](cc: Function22[T,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,R], values: List[_]): R = rec[T,R](cc.curried, values)
Example usage:
scala> case class MyCaseClass(a: Int, b: String, c: Double)
class MyCaseClass
scala> fill(MyCaseClass, List(42, "foo", 4.2))
val res1: MyCaseClass = MyCaseClass(42,foo,4.2)
This works, but unfortunately i couldn’t find another way to get a hold of R
(the type of the case class, aka the return type of the factory function), other than enumerating all the possible overloads Function1 … Function22
What do you think about this solution?
- is this what
.curried
was intended for ? - can the overloads (Function1…Function22) be avoided somehow ?
- if the overloads can’t be avoided, should i simply call the
.apply
method directly for each possible arity and not worry about the code bloat? As in:
def fill[T,R](cc: Function3[T1,T2,T3,R], vs: List[_]): R = cc.apply(
vs(0).asInstanceOf[T1],
vs(1).asInstanceOf[T2],
vs(2).asInstanceOf[T3])
- or, should i just use reflection and iterate on the
.apply
method
parameters? The overloads would still be needed to statically enforce the
return type, though…
Sorry for the dumb questions, i don’t have any formal CS education and my usage of Scala is pretty basic level. I’m curious about what more experienced Scala devs would do in this case.
Any suggestion or opinion is appreciated