Closed
Description
Hi! I was trying to learn how to derive typeclass instances with macros but the code from How to write a type class derived
method using macros does not compile with 3.0.0-M1.
Minimized example
import scala.deriving._
import scala.quoted._
import scala.quoted.matching._
trait Eq[T] {
def eqv(x: T, y: T): Boolean
}
object Eq {
given Eq[String] {
def eqv(x: String, y: String) = x == y
}
given Eq[Int] {
def eqv(x: Int, y: Int) = x == y
}
def eqProduct[T](body: (T, T) => Boolean): Eq[T] =
new Eq[T] {
def eqv(x: T, y: T): Boolean = body(x, y)
}
def eqSum[T](body: (T, T) => Boolean): Eq[T] =
new Eq[T] {
def eqv(x: T, y: T): Boolean = body(x, y)
}
def summonAll[T](t: Type[T])(using qctx: QuoteContext): List[Expr[Eq[_]]] = t match {
case '[String *: $tpes] => '{ summon[Eq[String]] } :: summonAll(tpes)
case '[Int *: $tpes] => '{ summon[Eq[Int]] } :: summonAll(tpes)
case '[$tpe *: $tpes] => derived(using tpe, qctx) :: summonAll(tpes)
case '[EmptyTuple] => Nil
}
given derived[T: Type](using qctx: QuoteContext) as Expr[Eq[T]] = {
import qctx.reflect._
val ev: Expr[Mirror.Of[T]] = Expr.summon(using '[Mirror.Of[T]]).get
ev match {
case '{ $m: Mirror.ProductOf[T] { type MirroredElemTypes = $elementTypes }} =>
val elemInstances = summonAll(elementTypes)
val eqProductBody: (Expr[T], Expr[T]) => Expr[Boolean] = (x, y) => {
elemInstances.zipWithIndex.foldLeft(Expr(true: Boolean)) {
case (acc, (elem, index)) =>
val e1 = '{$x.asInstanceOf[Product].productElement(${Expr(index)})}
val e2 = '{$y.asInstanceOf[Product].productElement(${Expr(index)})}
'{ $acc && $elem.asInstanceOf[Eq[Any]].eqv($e1, $e2) }
}
}
'{
eqProduct((x: T, y: T) => ${eqProductBody('x, 'y)})
}
case '{ $m: Mirror.SumOf[T] { type MirroredElemTypes = $elementTypes }} =>
val elemInstances = summonAll(elementTypes)
val eqSumBody: (Expr[T], Expr[T]) => Expr[Boolean] = (x, y) => {
val ordx = '{ $m.ordinal($x) }
val ordy = '{ $m.ordinal($y) }
val elements = Expr.ofList(elemInstances)
'{
$ordx == $ordy && $elements($ordx).asInstanceOf[Eq[Any]].eqv($x, $y)
}
}
'{
eqSum((x: T, y: T) => ${eqSumBody('x, 'y)})
}
}
}
}
object Macro3 {
extension [T](x: =>T)
inline def === (y: =>T)(using eq: Eq[T]): Boolean = eq.eqv(x, y)
implicit inline def eqGen[T]: Eq[T] = ${ Eq.derived[T] }
}
Output
[error] -- [E006] Not Found Error: /Users/pierre/GIT/dotty-example-project/src/main/scala/Test.scala:29:71
[error] 29 | case '[String *: $tpes] => '{ summon[Eq[String]] } :: summonAll(tpes)
[error] | ^^^^
[error] | Not found: tpes
[error] -- [E006] Not Found Error: /Users/pierre/GIT/dotty-example-project/src/main/scala/Test.scala:30:71
[error] 30 | case '[Int *: $tpes] => '{ summon[Eq[Int]] } :: summonAll(tpes)
[error] | ^^^^
[error] | Not found: tpes
[error] -- [E006] Not Found Error: /Users/pierre/GIT/dotty-example-project/src/main/scala/Test.scala:31:71
[error] 31 | case '[$tpe *: $tpes] => derived(using tpe, qctx) :: summonAll(tpes)
[error] | ^^^^
[error] | Not found: tpes
[error] -- [E006] Not Found Error: /Users/pierre/GIT/dotty-example-project/src/main/scala/Test.scala:31:47
[error] 31 | case '[$tpe *: $tpes] => derived(using tpe, qctx) :: summonAll(tpes)
[error] | ^^^
[error] | Not found: tpe
[warn] -- Warning: /Users/pierre/GIT/dotty-example-project/src/main/scala/Test.scala:38:53
[warn] 38 | val ev: Expr[Mirror.Of[T]] = Expr.summon(using '[Mirror.Of[T]]).get
[warn] | ^^^^^^^^^^^^^^^
[warn] |Consider using canonical type constructor scala.quoted.Type[Mirror.Of[T]] instead
[error] -- [E006] Not Found Error: /Users/pierre/GIT/dotty-example-project/src/main/scala/Test.scala:42:40
[error] 42 | val elemInstances = summonAll(elementTypes)
[error] | ^^^^^^^^^^^^
[error] | Not found: elementTypes
[error] -- [E006] Not Found Error: /Users/pierre/GIT/dotty-example-project/src/main/scala/Test.scala:57:40
[error] 57 | val elemInstances = summonAll(elementTypes)
[error] | ^^^^^^^^^^^^
[error] | Not found: elementTypes
[warn] one warning found
[error] 6 errors found