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
A context bound is a shorthand for expressing the common pattern of a context parameter that depends on a type parameter. Using a context bound, the `maximum` function of the last section can be written like this:
7
+
A context bound is a shorthand for expressing the common pattern of a context parameter that depends on a type parameter. These patterns are commonplace when modelling type classes in Scala. Using a context bound, the `maximum` function of the [last section](./using-clauses.md) can be written like this:
A bound like `: Ord` on a type parameter `T` of a method or class indicates a context parameter `using Ord[T]`. The context parameter(s) generated from context bounds
14
-
are added as follows:
13
+
A bound like `: Ord` on a type parameter `T` of a method or class indicates a context parameter `using Ord[T]`, which is added to the signature of the enclosing method. The generated parameter is called a _witness_ for the context bound.
15
14
16
-
- If the method parameters end in an implicit parameter list or using clause,
17
-
context parameters are added in front of that list.
18
-
- Otherwise they are added as a separate parameter clause at the end.
19
-
20
-
Example:
15
+
For instance the `maximum`method above expands to
16
+
```scala
17
+
defmaximum[T](xs: List[T])(usingOrd[T]):T= ...
18
+
```
19
+
Context bounds can be combined with subtype bounds. If both are present, subtype bounds come first, e.g.
21
20
22
21
```scala
23
-
deff[T:C1:C2, U:C3](x: T)(usingy: U, z: V):R
22
+
deff[T<:B:C](x: T):R= ...
24
23
```
25
24
26
-
would expand to
25
+
## Named Context Bounds
27
26
27
+
A context bound can be given a name with an `as` clause. For example, assume the following trait definitions.
28
+
```scala
29
+
traitSemiGroup[A]:
30
+
extension (x: A) defcombine(y: A):A
31
+
32
+
traitMonoid[A] extendsSemiGroup[A]:
33
+
defunit:A
34
+
```
35
+
We can write `reduce` function over lists of monoid instances like this:
We use `as x` after the type of a context bound to bind the instance to `x`. This is analogous to import renaming, which also introduces a new name for something that comes before.
31
41
32
-
Context bounds can be combined with subtype bounds. If both are present, subtype bounds come first, e.g.
42
+
In a context bound with a naming clause the witness parameter carries the given name. For instance the expanded signature of `reduce` would be
43
+
```scala
44
+
defreduce[A](xs: List[A])(usingm: Monoid[A]):A
45
+
```
46
+
Since the context parameter now has a name, it can be referred
47
+
to in the body of `reduce`. An example is the `m.unit` reference in the definition above.
48
+
49
+
If the context bound does not carry an `as` clause, the generated witness parameter gets a compiler-synthesized name. However, a [currently experimental
50
+
language extension](../experimental/default-names-context-bounds.md) would in this case give the context parameter the same name as the bound type parameter.
51
+
52
+
Named context bounds were introduced in Scala 3.6.
53
+
54
+
## Aggregate Context Bounds
33
55
56
+
A type parameter can have several context bounds. If there are multiple bounds, they are written inside braces `{...}`. Example:
This syntax is valid from Scala 3.6. The previous syntax used
66
+
chains of `:` clauses, as in `[X : Ord : Show]`. This syntax is still available but will be deprecated and removed over time.
39
67
40
-
To ease migration, context bounds in Dotty map in Scala 3.0 to old-style implicit parameters
41
-
for which arguments can be passed either with a `(using ...)` clause or with a normal application. From Scala 3.1 on, they will map to context parameters instead, as is described above.
68
+
## Placement of Generated Context Parameters
42
69
43
-
If the source version is `future-migration`, any pairing of an evidence
70
+
The witness context parameter(s) generated from context bounds are added as follows:
71
+
72
+
1. If one of the bounds is referred to by its name in a subsequent parameter clause, the context bounds are mapped to a using clause immediately preceding the first such parameter clause.
73
+
2. Otherwise, if the last parameter clause is a using (or implicit) clause, merge all parameters arising from context bounds in front of that clause, creating a single using clause.
74
+
3. Otherwise, let the parameters arising from context bounds form a new using clause at the end.
75
+
76
+
Rules (2) and (3) match Scala 2's rules. Rule (1) is new but since context bounds so far could not be referred to, it does not apply to legacy code. Therefore, binary compatibility with Scala 2 and earlier Scala 3 versions is maintained.
Note that the `using` clause is placed in front of the explicit parameter clause `(in: p.Result)` so that
120
+
the type`p.Result` can legally refer to the context parameter `p`.
121
+
122
+
###Migration
123
+
124
+
To ease migration, context bounds map in Scala3.0-3.5 to old-style implicit parameters
125
+
for which arguments can be passed either with a `(using ...)` clause or with a normal application. FromScala3.6 on, they will map to context parameters instead, asis described above.
126
+
127
+
If the source version is `3.6-migration`, any pairing of an evidence
44
128
context parameter stemming from a context bound with a normal argument will give a migration
45
129
warning. The warning indicates that a `(using ...)` clause is needed instead. The rewrite can be
46
130
done automatically under `-rewrite`.
47
131
132
+
##ContextBoundsforPolymorphicFunctions
133
+
134
+
FromScala3.6 on, context bounds can also be used in polymorphic function types and polymorphic function literals:
135
+
136
+
```scala
137
+
typeComparer= [X:Ord] => (x: X, y: X) =>Boolean
138
+
valless:Comparer= [X:Ordasord] => (x: X, y: X) =>
139
+
ord.compare(x, y) <0
140
+
```
141
+
142
+
The expansion of such context bounds is analogous to the expansion in method types, except that instead of adding a using clause in a method, we insert a [context function type](./context-functions.md).
143
+
144
+
For instance, the `type` and `val` definitions above would expand to
The expansion of using clauses does look inside alias types. For instance,
152
+
here is a variation of the previous example that uses a parameterized type alias:
153
+
```scala
154
+
typeCmp[X] = (x: X, y: X) =>Ord[X] ?=>Boolean
155
+
typeComparer2= [X:Ord] =>Cmp[X]
156
+
```
157
+
The expansion of the right hand side of `Comparer2` expands the `Cmp[X]` alias
158
+
and then inserts the context function at the same place as what's done for `Comparer`.
159
+
160
+
### Context Bounds for Type Members
161
+
162
+
From Scala 3.6 on, context bounds can not only used for type parameters but also for abstract type members.
163
+
164
+
**Example**:
165
+
166
+
```scala
167
+
classCollection:
168
+
typeElement:Ord
169
+
```
170
+
171
+
These context bounds have to expand differently from context bounds for type parameters since there is no parameter list to accommodate any generated witnesses. Instead, context bounds for abstract types map to
172
+
[deferred givens](./deferred-givens.md).
173
+
174
+
For instance, the `Collection` class above expands to:
175
+
```scala
176
+
classCollection:
177
+
typeElement
178
+
givenOrd[Element] = deferred
179
+
```
180
+
As is explain in the [section on deferred givens](./deferred-givens.md), `deferred` is a special name defined in the `scala.compiletime` package.
Scala 3.6 introduces a new way to implement a given definition in a trait like this:
8
+
```scala
9
+
givenT= deferred
10
+
```
11
+
Such givens can be implemented automatically in subclasses. `deferred` is a new method in the `scala.compiletime` package, which can appear only as the right hand side of a given defined in a trait. Any class implementing that trait will provide an implementation of this given. If a definition is not provided explicitly, it will be synthesized by searching for a given of type `T` in the scope of the inheriting class. Specifically, the scope in which this given will be searched is the environment of that class augmented by its parameters but not containing its members (since that would lead to recursive resolutions). If an implementation _is_ provided explicitly, it counts as an override of a concrete definition and needs an `override` modifier.
12
+
13
+
Deferred givens allow a clean implementation of context bounds in traits,
14
+
as in the following example:
15
+
```scala
16
+
traitSorted:
17
+
typeElement:Ord
18
+
19
+
classSortedSet[A:Ordasord] extendsSorted:
20
+
typeElement=A
21
+
```
22
+
The compiler expands this to the following implementation.
The using clause in class `SortedSet` provides an implementation for the deferred given in trait `Sorted`.
34
+
35
+
One can also provide an explicit implementation of a deferred given, as in the following example:
36
+
37
+
```scala
38
+
classSortedString[A] extendsSorted:
39
+
typeElement=String
40
+
overridegivenOrd[String] = ...
41
+
```
42
+
43
+
Note that the implementing given needs an `override` modifier since the `deferred` given in class `Sorted` counts as a concrete (i.e. not abstract) definition. In a sense, the `deferred` right hand side in `Sorted` is like a (magic, compiler-supported) macro, with the peculiarity that the macro's implementation also affects subclasses.
44
+
45
+
## Abstract Givens
46
+
47
+
A given may also be an abstract member, with the restriction that it must have an explicit name. Example:
48
+
49
+
```scala
50
+
traitHasOrd[T]:
51
+
givenord:Ord[T]
52
+
```
53
+
An abstract given has the form `given name: Type` without a right-hand side or arguments to the type.
54
+
55
+
Since Scala 3.6, abstract givens are made redundant by deferred givens. Deferred givens can replace abstract givens. They have better ergonomics, since deferred givens get naturally implemented in inheriting classes, so there is no longer any need for boilerplate to fill in definitions of abstract givens.
56
+
57
+
It is therefore recommended that software architectures relying on abstract givens be migrated to use deferred givens instead. Abstract givens are still supported in Scala 3.6, but will likely be deprecated and phased out over time.
0 commit comments