Skip to content

Code from docs does not compile (typeclass derivation with macros) #10381

Closed
@ghostdogpr

Description

@ghostdogpr

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions