You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
In the main [derivation](./derivation.md) documentation page, we explained the
8
-
details behind `Mirror`s and type class derivation. Here we demonstrate how to
9
-
implement a type class `derived` method using macros only. We follow the same
10
-
example of deriving `Eq` instances and for simplicity we support a `Product`
11
-
type e.g., a case class `Person`. The low-level method we will use to implement
12
-
the `derived` method exploits quotes, splices of both expressions and types and
13
-
the `scala.quoted.Expr.summon` method which is the equivalent of
14
-
`summonFrom`. The former is suitable for use in a quote context, used within
15
-
macros.
7
+
In the main [derivation](./derivation.md) documentation page, we explained the details behind `Mirror`s and type class derivation.
8
+
Here we demonstrate how to implement a type class `derived` method using macros only.
9
+
We follow the same example of deriving `Eq` instances and for simplicity we support a `Product` type e.g., a case class `Person`.
10
+
The low-level technique that we will use to implement the `derived` method exploits quotes, splices of both expressions and types and the `scala.quoted.Expr.summon` method which is the equivalent of `scala.compiletime.summonFrom`.
11
+
The former is suitable for use in a quote context, used within macros.
16
12
17
13
As in the original code, the type class definition is the same:
18
14
@@ -21,185 +17,175 @@ trait Eq[T]:
21
17
defeqv(x: T, y: T):Boolean
22
18
```
23
19
24
-
we need to implement a method `Eq.derived` on the companion object of `Eq` that
25
-
produces a quoted instance for `Eq[T]`. Here is a possible signature,
20
+
We need to implement an inline method `Eq.derived` on the companion object of `Eq` that calls into a macro to produce a quoted instance for `Eq[T]`.
and for comparison reasons we give the same signature we had with `inline`:
30
+
Note, that since a type is used in a subsequent macro compilation stage it will need to be lifted to a `quoted.Type` by using the corresponding context bound (seen in `derivedMacro`).
31
+
32
32
33
+
For comparison, here is the signature of the inline `derived` method from the [main derivation page](./derivation.md):
Note, that in the `inline` case we can merely write
68
-
`summonAll[m.MirroredElemTypes]` inside the inline method but here, since
69
-
`Expr.summon` is required, we can extract the element types in a macro fashion.
70
-
Being inside a macro, our first reaction would be to write the code below. Since
71
-
the path inside the type argument is not stable this cannot be used:
66
+
Note, that in the version without macros, we can merely write `summonInstances[T, m.MirroredElemTypes]` inside the inline method but here, since `Expr.summon` is required, we can extract the element types in a macro fashion.
67
+
Being inside a macro, our first reaction would be to write the code below:
72
68
73
69
```scala
74
70
'{
75
-
summonAll[$m.MirroredElemTypes]
71
+
summonInstances[T, $m.MirroredElemTypes]
76
72
}
77
73
```
78
74
79
-
Instead we extract the tuple-type for element types using pattern matching over
80
-
quotes and more specifically of the refined type:
75
+
However, since the path inside the type argument is not stable this cannot be used.
76
+
Instead we extract the tuple-type for element types using pattern matching over quotes and more specifically of the refined type:
Shown below is the implementation of `summonInstances` as a macro, which for each type `elem` in the tuple type, calls
83
+
`deriveOrSummon[T, elem]`.
97
84
98
-
One additional difference with the body of `derived` here as opposed to the one
99
-
with `inline` is that with macros we need to synthesize the body of the code during the
100
-
macro-expansion time. That is the rationale behind the `eqProductBody` function.
101
-
Assuming that we calculate the equality of two `Person`s defined with a case
102
-
class that holds a name of type [`String`](https://scala-lang.org/api/3.x/scala/Predef$.html#String-0)
103
-
and an age of type `Int`, the equality check we want to generate is the following:
85
+
To understand `deriveOrSummon`, consider that if `elem` derives from the parent `T` type, then it is a recursive derivation.
86
+
Recursive derivation usually happens for types such as `scala.collection.immutable.::`. If `elem` does not derive from `T`, then there must exist a contextual `Eq[elem]` instance.
case _ => derivedMacro[Elem] // recursive derivation
123
104
```
124
105
125
-
Note, that we use inline method syntax and we can compare instance such as
126
-
`Sm(Person("Test", 23)) === Sm(Person("Test", 24))` for e.g., the following two
127
-
types:
106
+
One additional difference with the body of `derivedMacro` here as opposed to the one with `inline` is that with macros we need to synthesize the body of the code during the macro-expansion time.
107
+
That is the rationale behind the `eqProductBody` function.
108
+
Assuming that we calculate the equality of two `Person`s defined with a case class that holds a name of type [`String`](https://scala-lang.org/api/3.x/scala/Predef$.html#String-0) and an age of type `Int`, the equality check we want to generate is the following:
0 commit comments