Scala 3:派生类型 class 以在大小写 class 和元组类型之间进行转换

Scala 3: Deriving a typeclass for conversion between a case class and a tuple type

我知道在 Scala 3 中现在可以使用 Tuple.fromProductTypedsummon[Mirror.Of].fromProduct.

在大小写 class 实例和元组之间进行转换

我希望能够定义一个类型class,它见证了案例 class T 可以使用上述方法与 (A, B) 相互转换.

我已经这样定义了类型class:

trait IsPair[T, A, B]:
  def toTuple(t: T): (A, B)
  def fromTuple(ab: (A, B)): T

对于任何符合条件的情况 class,我们如何推导出 IsPair

推导到位后,以下内容应该可以编译并且 运行 没有错误:

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)

您可以只使用 Mirror 但将其限制为 pair-like 案例 类:

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

这样我们就可以做到:

val foo = Foo(42, "qwerty")
val tuple = (42, "qwerty")

val fooIsPair = IsPair[Foo]

assert(fooIsPair.toTuple(foo) == tuple)
assert(fooIsPair.fromTuple(tuple) == foo)

可以看到代码运行 here.