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
Copy file name to clipboardExpand all lines: docs/_docs/reference/contextual/derivation.md
+85-61Lines changed: 85 additions & 61 deletions
Original file line number
Diff line number
Diff line change
@@ -309,87 +309,105 @@ worked out example of such a library, see [Shapeless 3](https://github.com/miles
309
309
310
310
## How to write a type class `derived` method using low level mechanisms
311
311
312
-
The low-level method we will use to implement a type class `derived` method in this example exploits three new
313
-
type-level constructs in Scala 3: inline methods, inline matches, and implicit searches via `summonInline` or `summonFrom`. Given this definition of the
314
-
`Eq` type class,
312
+
The low-level method we will use to implement a type class `derived` method in this example exploits three new type-level constructs in Scala 3: inline methods, inline matches, and implicit searches via `summonInline` or `summonFrom`.
313
+
Given this definition of the `Eq` type class,
315
314
316
315
```scala
317
316
traitEq[T]:
318
317
defeqv(x: T, y: T):Boolean
319
318
```
320
319
321
320
we need to implement a method `Eq.derived` on the companion object of `Eq` that produces a given instance for `Eq[T]` given
Note that `derived` is defined as an `inline` given. This means that the method will be expanded at
335
-
call sites (for instance the compiler generated instance definitions in the companion objects of ADTs which have a
336
-
`derived Eq` clause), and also that it can be used recursively if necessary, to compute instances for children.
334
+
Note that `derived` is defined as an `inline def`.
335
+
This means that the method will be expanded at all call sites (for instance the compiler generated instance definitions in the companion objects of ADTs which have a `deriving Eq` clause).
337
336
338
-
The body of this method (1) first materializes the `Eq` instances for all the child types of type the instance is
339
-
being derived for. This is either all the branches of a sum type or all the fields of a product type. The
340
-
implementation of `summonAll` is `inline` and uses Scala 3's `summonInline` construct to collect the instances as a
341
-
`List`,
337
+
> Expansion is potentially expensive if overused (meaning slower compile times) so we should be careful to limit how many times `derived` is called for the same type.
338
+
> For example, when computing an instance for a sum type, it may be necessary to call `derived` recursively to compute an instance for a one of its child cases.
339
+
> That child case may in turn be a product type, that declares a field referring back to the parent sum type.
340
+
> To compute the instance for this field, we should not call `derived` recursively, but instead summon from the context.
341
+
> Typically the found given instance will be the root given instance that initially called `derived`.
342
+
343
+
The body of `derived` (1) first materializes the `Eq` instances for all the child types of type the instance is being derived for.
344
+
This is either all the branches of a sum type or all the fields of a product type.
345
+
The implementation of `summonInstances` is `inline` and uses Scala 3's `summonInline` construct to collect the instances as a `List`,
case _ =>Eq.derived[Elem](using summonInline[Mirror.Of[Elem]]) // recursive derivation
348
362
```
349
363
350
364
with the instances for children in hand the `derived` method uses an `inline match` to dispatch to methods which can
351
-
construct instances for either sums or products (2). Note that because `derived` is `inline` the match will be
352
-
resolved at compile-time and only the left-hand side of the matching case will be inlined into the generated code with
353
-
types refined as revealed by the match.
365
+
construct instances for either sums or products (2).
366
+
Note that because `derived` is `inline` the match will be resolved at compile-time and only the right-hand side of the matching case will be inlined into the generated code with types refined as revealed by the match.
354
367
355
-
In the sum case, `eqSum`, we use the runtime `ordinal` values of the arguments to `eqv` to first check if the two
356
-
values are of the same subtype of the ADT (3) and then, if they are, to further test for equality based on the `Eq`
357
-
instance for the appropriate ADT subtype using the auxiliary method `check` (4).
368
+
In the sum case, `eqSum`, we use the runtime `ordinal` values of the arguments to `eqv` to first check if the two values are of the same subtype of the ADT (3) and then, if they are, to further test for equality based on the `Eq` instance for the appropriate ADT subtype using the auxiliary method `check` (4).
(s.ordinal(y) == ordx) && check(x, y, elems(ordx)) // (4)
367
378
```
368
379
369
-
In the product case, `eqProduct` we test the runtime values of the arguments to `eqv` for equality as products based
370
-
on the `Eq` instances for the fields of the data type (5),
380
+
In the product case, `eqProduct` we test the runtime values of the arguments to `eqv` for equality as products based on the `Eq` instances for the fields of the data type (5),
0 commit comments