I am aware that in Scala 3 it is now possible to convert between case class instances and tuples using Tuple.fromProductTyped
and summon[Mirror.Of].fromProduct
.
I would like to be able to define a typeclass that witnesses that a case class T
can be converted to and from (A, B)
using the aforementioned methods.
I've defined the typeclass like so:
trait IsPair[T, A, B]:
def toTuple(t: T): (A, B)
def fromTuple(ab: (A, B)): T
How can we derive IsPair
for any conforming case class?
With the derivation in place, the following should compile and run without error:
case class Foo(a: Int, b: String)
val fooIsPair = summon[IsPair[Foo, Int, String]]
val foo = Foo(42, "qwerty")
val tuple = (42, "qwerty")
assert(fooIsPair.toTuple(foo) == tuple)
assert(fooIsPair.fromTuple(tuple) == foo)
CodePudding user response:
You can just use a Mirror
but restrict it to be for pair-like case classes:
import scala.deriving.Mirror
sealed trait IsPair[T]:
type A
type B
def toTuple(t: T): (A, B)
def fromTuple(ab: (A, B)): T
object IsPair:
type Aux[T, X, Y] = IsPair[T] { type A = X; type B = Y }
inline def apply[T](using ev: IsPair[T]): ev.type = ev
given instance[T <: Product, X, Y](using
ev: Mirror.Product { type MirroredType = T; type MirroredMonoType = T; type MirroredElemTypes = (X, Y) }
): IsPair[T] with
override final type A = X
override final type B = Y
override def toTuple(t: T): (X, Y) =
Tuple.fromProductTyped(t)
override def fromTuple(xy: (X, Y)): T =
ev.fromProduct(xy)
end IsPair
That way we can do this:
val foo = Foo(42, "qwerty")
val tuple = (42, "qwerty")
val fooIsPair = IsPair[Foo]
assert(fooIsPair.toTuple(foo) == tuple)
assert(fooIsPair.fromTuple(tuple) == foo)
You can see the code running here.