Suppose
case class Person(name: String, age: Int)
Then
summon[deriving.Mirror.Of[Person]].fromProduct("Sh", 43)
will compile and can be used to turn a Tuple
to a case class
. But this is not possible when I want to create a generic method based on this. i.e.
def tuple2GenericCaseClass[C <: Product](tuple: Tuple): C = summon[deriving.Mirror.Of[C]].fromProduct(tuple)
does not compile. I want something like tuple2GenericCaseClass[Person]("Sh", 43)
to work.
Actually my problem is not being able to convert tuples to case classes, since I could easily make it work by a little signature modification and an implicit conversion for each case class in scope i.e,
given Conversion[(String, Int), Person] = (tuple: (String, Int)) => Person.apply.tupled(tuple)
def tuple2GenericCaseClass[C <: Product](c: C): C = c
will let me use the same tuple2GenericCaseClass[Person]("Sh", 43)
with expected results. I want to know why summon
does not compile for generic case classes and information on how to fix it if possible?
Well, I assume this doesn’t compile because there is no type information to derive the Mirror
there.
You can easily fix that doing this:
import deriving.Mirror
def tuple2GenericCaseClass[C <: Product, T <: Tuple](tuple: T)(using mirror: Mirror.ProductOf[C] { MirroredElemTypes = T }): C =
mirror.fromTuple(tuple)
But, that is basically just repeating what fromTuple
already does.
And also means that if you call this on a generic context you need to keep preserving the type information all the layers up until you have something concrete.
Another option would be to make it an inline
method, so the types and implicit get resolved at the call site. But, once again, if you call this in a generic context, then you need to keep inlining
until it is concrete, which can lead to a lot of bytecode generation.
In other words, you can’t just make a generic method that does that operation, hiding the Mirror
since the Mirror
is the one that actually knows how to do that operation and the compiler can only generate mirrors for concrete types.
4 Likes
Mirror.ProductOf[T]
gives a generic representation for the case class T
.
m.fromProduct(tuple)
builds the case class from a tuple using the product constructor.
- The program includes a
@main
method for running as a standalone app.
import scala.deriving.Mirror
import scala.compiletime.{erasedValue, summonInline}
object Tuples2CaseClass:
case class Person(name: String, age: Int)
inline def tupleToCaseClass[T](tuple: Tuple)(using m: Mirror.ProductOf[T]): T =
m.fromProduct(tuple)
@main def runTuples2CaseClass(args: String*): Unit =
println("File /Users/drmark/IdeaProjects/PLANE/src/main/scala/scala3Features/Tuples2CaseClass.scala created at time 7:24AM")
val data = ("Alice", 30)
val person = tupleToCaseClass[Person](data)
println(person)
2 Likes
This solves my problem. I remember trying to use an implicit parameter, with no success. Probably I got confused on how to correctly externalize the summon
part as an implicit parameter because I missed the point that the type of using
part should be generic based on the case class. Anyway, thanks dear @0x1DOCD00D .