Skip to content

Commit 53b5ddf

Browse files
biboudisliufengyun
andcommitted
Add macro-based implementation of the typeclass derivation example
Co-authored-by: Fengyun Liu <[email protected]>
1 parent 597d4ca commit 53b5ddf

File tree

5 files changed

+98
-14
lines changed

5 files changed

+98
-14
lines changed

tests/run-macros/i8007.check

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
11
List("name", "age")
22

33
Test 23
4+
()
5+
6+
true
7+
8+
false
9+

tests/run-macros/i8007/Macro_1.scala

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,9 @@ object Macro1 {
1212
case '[Unit] => Nil
1313
}
1414

15-
// Macro method 1
16-
// Demonstrates the use of quoted pattern matching
17-
// over a refined type, extracting the tuple type
18-
// for e.g., MirroredElemLabels
15+
// Demonstrates the use of quoted pattern matching
16+
// over a refined type extracting the tuple type
17+
// for e.g., MirroredElemLabels
1918
inline def test1[T](value: =>T): List[String] =
2019
${ test1Impl('value) }
2120

tests/run-macros/i8007/Macro_3.scala

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import scala.deriving._
2+
import scala.quoted._
3+
import scala.quoted.matching._
4+
import scala.compiletime.{erasedValue, summonFrom, constValue}
5+
6+
object Macro3 {
7+
8+
trait Eq[T] {
9+
def eqv(x: T, y: T): Boolean
10+
}
11+
12+
object Eq {
13+
given Eq[String] {
14+
def eqv(x: String, y: String) = x == y
15+
}
16+
17+
given Eq[Int] {
18+
def eqv(x: Int, y: Int) = x == y
19+
}
20+
21+
def eqProduct[T](body: (T, T) => Boolean): Eq[T] =
22+
new Eq[T] {
23+
def eqv(x: T, y: T): Boolean = body(x, y)
24+
}
25+
26+
def summonAll[T](t: Type[T])(given qctx: QuoteContext): List[Expr[Eq[_]]] = t match {
27+
case '[$tpe *: $tpes] => summonExpr(given '[Eq[$tpe]]).get :: summonAll(tpes)
28+
case '[Unit] => Nil
29+
}
30+
31+
def derived[T: Type](ev: Expr[Mirror.Of[T]])(given qctx: QuoteContext): Expr[Eq[T]] = {
32+
import qctx.tasty.{_, given}
33+
34+
val elementTypes = ev match {
35+
case '{ $m: Mirror.ProductOf[T] { type MirroredElemTypes = $elem } } => elem
36+
}
37+
38+
val elemInstances = summonAll(elementTypes)
39+
40+
val eqProductBody: (Expr[T], Expr[T]) => Expr[Boolean] = (x, y) => {
41+
elemInstances.zipWithIndex.foldLeft(Expr(true: Boolean)) {
42+
case (acc, (elem, index)) =>
43+
val e1 = '{$x.asInstanceOf[Product].productElement(${Expr(index)})}
44+
val e2 = '{$y.asInstanceOf[Product].productElement(${Expr(index)})}
45+
'{ $acc && $elem.asInstanceOf[Eq[Any]].eqv($e1, $e2) }
46+
}
47+
}
48+
49+
'{
50+
eqProduct((x: T, y: T) => ${eqProductBody('x, 'y)})
51+
}
52+
}
53+
}
54+
55+
inline def test3[T](value: =>T, value2: =>T): Boolean = ${ test3Impl('value, 'value2) }
56+
57+
def test3Impl[T: Type](value: Expr[T], value2: Expr[T])(given qctx: QuoteContext): Expr[Boolean] = {
58+
import qctx.tasty.{_, given}
59+
60+
val mirrorTpe = '[Mirror.Of[T]]
61+
val mirrorExpr = summonExpr(given mirrorTpe).get
62+
val derivedInstance = Eq.derived(mirrorExpr)
63+
64+
'{
65+
$derivedInstance.eqv($value, $value2)
66+
}
67+
}
68+
}

tests/run-macros/i8007/Test_3.scala

Lines changed: 0 additions & 10 deletions
This file was deleted.

tests/run-macros/i8007/Test_4.scala

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import Macro1._
2+
import Macro2._
3+
import Macro3._
4+
5+
@main def Test() = {
6+
val t1 = test1(Person("Test", 23))
7+
println(t1)
8+
println
9+
10+
val t2 = test2(Person("Test", 23))
11+
println(t2)
12+
println
13+
14+
val t3 = test3(Person("Test", 23), Person("Test", 23))
15+
println(t3) // true
16+
println
17+
18+
val t4 = test3(Person("Test", 23), Person("Test", 24))
19+
println(t4) // false
20+
println
21+
}

0 commit comments

Comments
 (0)