Skip to content

Fix example code from How to write a type class derived method using macros #10382

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Nov 19, 2020
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 22 additions & 22 deletions docs/docs/reference/contextual/derivation-macro.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,11 @@ from the signature. The body of the `derived` method is shown below:
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
val ev: Expr[Mirror.Of[T]] = Expr.summon(using Type.of[Mirror.Of[T]]).get

ev match {
case '{ $m: Mirror.ProductOf[T] { type MirroredElemTypes = $elementTypes }} =>
val elemInstances = summonAll(elementTypes)
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)) =>
Expand Down Expand Up @@ -84,17 +84,17 @@ Instead we extract the tuple-type for element types using pattern matching over
quotes and more specifically of the refined type:

```scala
case '{ $m: Mirror.ProductOf[T] { type MirroredElemTypes = $elementTypes } } => ...
case '{ $m: Mirror.ProductOf[T] { type MirroredElemTypes = elementTypes }} => ...
```

The implementation of `summonAll` as a macro can be show below assuming that we
have the given instances for our primitive types:

```scala
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)
def summonAll[T: Type](using qctx: QuoteContext): List[Expr[Eq[_]]] = t match {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nicolasstucki Does this actually compile? I don't see where t is defined here. Also if we update the doc we should keep the test case synchronized: https://github.com/lampepfl/dotty/blob/master/tests/run-macros/i8007/Macro_3.scala

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I missed that. I will fix it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix in #10395

case '[String *: tpes] => '{ summon[Eq[String]] } :: summonAll[tpes]
case '[Int *: tpes] => '{ summon[Eq[Int]] } :: summonAll[tpes]
case '[tpe *: tpes] => derived[tpe] :: summonAll[tpes]
case '[EmptyTuple] => Nil
}
```
Expand All @@ -120,8 +120,8 @@ Alternatively and what is shown below is that we can call the `eqv` method
directly. The `eqGen` can trigger the derivation.

```scala
extension [T](x: =>T)
inline def === (y: =>T)(using eq: Eq[T]): Boolean = eq.eqv(x, y)
extension [T](inline x: T)
inline def === (inline y: T)(using eq: Eq[T]): Boolean = eq.eqv(x, y)

implicit inline def eqGen[T]: Eq[T] = ${ Eq.derived[T] }
```
Expand All @@ -144,7 +144,7 @@ The full code is shown below:
```scala
import scala.deriving._
import scala.quoted._
import scala.quoted.matching._


trait Eq[T] {
def eqv(x: T, y: T): Boolean
Expand All @@ -169,21 +169,21 @@ object Eq {
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)
def summonAll[T: Type](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[tpe] :: 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
val ev: Expr[Mirror.Of[T]] = Expr.summon(using Type.of[Mirror.Of[T]]).get

ev match {
case '{ $m: Mirror.ProductOf[T] { type MirroredElemTypes = $elementTypes }} =>
val elemInstances = summonAll(elementTypes)
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)) =>
Expand All @@ -197,8 +197,8 @@ object Eq {
eqProduct((x: T, y: T) => ${eqProductBody('x, 'y)})
}

case '{ $m: Mirror.SumOf[T] { type MirroredElemTypes = $elementTypes }} =>
val elemInstances = summonAll(elementTypes)
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) }
Expand All @@ -217,8 +217,8 @@ object Eq {
}

object Macro3 {
extension [T](x: =>T)
inline def === (y: =>T)(using eq: Eq[T]): Boolean = eq.eqv(x, y)
extension [T](inline x: T)
inline def === (inline y: T)(using eq: Eq[T]): Boolean = eq.eqv(x, y)

implicit inline def eqGen[T]: Eq[T] = ${ Eq.derived[T] }
}
Expand Down