Skip to content

Commit 513a645

Browse files
committed
Apply suggestions from code review
1 parent 7131bd4 commit 513a645

File tree

1 file changed

+37
-12
lines changed

1 file changed

+37
-12
lines changed

docs/_docs/reference/contextual/derivation.md

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ movedTo: https://docs.scala-lang.org/scala3/reference/contextual/derivation.html
55
---
66

77
Type class derivation is a way to automatically generate given instances for type classes which satisfy some simple
8-
conditions. A type class in this sense is any trait or class with a type parameter determining the type being operated
9-
on. Common examples are `Eq`, `Ordering`, or `Show`. For example, given the following `Tree` algebraic data type
8+
conditions. A type class in this sense is any trait or class with a single type parameter determining the type being operated
9+
on, and the special case `CanEqual`. Common examples are `Eq`, `Ordering`, or `Show`. For example, given the following `Tree` algebraic data type
1010
(ADT):
1111

1212
```scala
@@ -35,7 +35,7 @@ given [T: Ordering]: Ordering[Option[T]] = Ordering.derived
3535
It is discouraged to directly refer to the `derived` member if you can use a `derives` clause instead.
3636

3737
## Exact mechanism
38-
In the following, a parameter enumerations where the first index is bigger than the last means there is actually no paramers, for example: `A[T_2, ..., T_1]` means `A`.
38+
In the following, when type arguments are enumerated and the first index evaluates to a larger value than the last, then there are actually no arguments, for example: `A[T_2, ..., T_1]` means `A`.
3939

4040
For a class/trait/object/enum `DerivingType[T_1, ..., T_N] derives TC`, a derived instance is created in `DerivingType`'s companion object (or `DerivingType` itself if it is an object).
4141

@@ -47,11 +47,12 @@ given [...]: TC[ ... DerivingType[...] ... ] = TC.derived
4747

4848
**Note:** `TC.derived` is a normal access, therefore if there are multiple definitions of `TC.derived`, overloading resolution applies.
4949

50-
What the derived instance precisely looks like depends on the specifics of `DerivingType` and `TC`, the first condition is the arity of `TC`:
50+
What the derived instance precisely looks like depends on the specifics of `DerivingType` and `TC`, we first examine `TC`:
5151

5252
### `TC` takes 1 parameter
5353

54-
Therefore `TC` is defined as `TC[F[A_1, ..., A_K]]` (`TC[F]` if `K == 0`), there are two further cases depending on the `A_i`s:
54+
Therefore `TC` is defined as `TC[F[A_1, ..., A_K]]` (`TC[F]` if `K == 0`) for some `F`.
55+
There are two further cases depending on the kinds of arguments:
5556

5657
#### `F` and all arguments of `DerivingType` have kind `*`
5758

@@ -60,18 +61,31 @@ The generated instance is then:
6061
given [T_1: TC, ..., T_N: TC]: TC[DerivingType[T_1, ..., T_N]] = TC.derived
6162
```
6263

64+
This is the most common case, and is the one that was highlighted in the introduction.
65+
6366
**Note:** If `N == 0` the above means:
6467
```scala
6568
given TC[DerivingType] = TC.derived
6669
```
70+
For example, the class
71+
```scala
72+
case class Point(x: Int, y: Int) derives Ordering
73+
```
74+
generates the instance
75+
```scala
76+
object Point:
77+
...
78+
given Ordering[Point] = Ordering.derived
79+
```
80+
6781

6882
#### `F` and `DerivingType` have parameters of matching kind on the right
69-
This section concers cases where you can pair arguments of `F` and `DerivingType` starting from the right such that they have the same kinds pairwise, and all arguments of `F` or `DerivingType` (or both) are used up.
83+
This section covers cases where you can pair arguments of `F` and `DerivingType` starting from the right such that they have the same kinds pairwise, and all arguments of `F` or `DerivingType` (or both) are used up.
7084
`F` must also have at least one parameter.
7185

7286
The general shape will then be:
7387
```scala
74-
given [...]: TC[ [...] => DerivingType[...] ] = TC.derived
88+
given [...]: TC[ [...] =>> DerivingType[...] ] = TC.derived
7589
```
7690
Where of course `TC` and `DerivingType` are applied to types of the correct kind.
7791

@@ -96,10 +110,7 @@ If `TC` takes less arguments than `DerivingType` (`K < N`), we fill in the leftm
96110
given [T_1, ... T_(N-K)]: TC[[A_1, ..., A_K] =>> DerivingType[T_1, ... T_(N-K), A_1, ..., A_K]] = TC.derived
97111
```
98112

99-
### `TC` takes 2 parameters
100-
101-
Is `TC` the `CanEqual` type class ?
102-
If yes, proceed as follows, otherwise go to [`TC` is not valid for automatic derivation](#tc-is-not-valid-for-automatic-derivation).
113+
### `TC` is the `CanEqual` type class
103114

104115
We have therefore: `DerivingType[T_1, ..., T_N] derives CanEqual`.
105116

@@ -115,9 +126,23 @@ given [T_1L, T_1R, ..., T_NL, T_NR] // every paramete
115126

116127
The bounds of `T_i`s are handled correctly, for example: `T_2 <: T_1` becomes `T_2L <: T_1L`.
117128

129+
For example, the class
130+
```scala
131+
class MyClass[A, G[_]](a: A, b: G[B]) derives CanEqual
132+
```
133+
generates the following given instance:
134+
```scala
135+
object MyClass:
136+
...
137+
given [A_L, A_R, G_L[_], G_R[_]](using CanEqual[A_L, B_L]): CanEqual[MyClass[A_L, G_L], MyClass[A_R, G_R]] = CanEqual.derived
138+
```
139+
118140
### `TC` is not valid for automatic derivation
119141

120-
Throw some error.
142+
Throw an error.
143+
144+
The exact error depends on which of the above conditions failed.
145+
As an example, if `TC` takes more than 1 parameter and is not `CanEqual`, the error is `DerivingType cannot be unified with the type argument of TC`.
121146

122147
All data types can have a `derives` clause. The rest of this document focuses primarily on data types which also have a given instance
123148
of the `Mirror` type class available.

0 commit comments

Comments
 (0)