Skip to content

Commit 0c721dc

Browse files
committed
Update docs to new conventions
Also: Some reshuffling and editing of existing material.
1 parent ca6b8f7 commit 0c721dc

22 files changed

+764
-655
lines changed

docs/_docs/reference/contextual/by-name-context-parameters.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ trait Codec[T]:
1212

1313
given intCodec: Codec[Int] = ???
1414

15-
given optionCodec[T](using ev: => Codec[T]): Codec[Option[T]] with
15+
given optionCodec: [T] => (ev: => Codec[T]) => Codec[Option[T]]:
1616
def write(xo: Option[T]) = xo match
1717
case Some(x) => ev.write(x)
1818
case None =>

docs/_docs/reference/contextual/context-bounds.md

+174-19
Original file line numberDiff line numberDiff line change
@@ -4,50 +4,205 @@ title: "Context Bounds"
44
nightlyOf: https://docs.scala-lang.org/scala3/reference/contextual/context-bounds.html
55
---
66

7-
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:
88

99
```scala
1010
def maximum[T: Ord](xs: List[T]): T = xs.reduceLeft(max)
1111
```
1212

13-
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.
1514

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+
def maximum[T](xs: List[T])(using Ord[T]): T = ...
18+
```
19+
Context bounds can be combined with subtype bounds. If both are present, subtype bounds come first, e.g.
2120

2221
```scala
23-
def f[T: C1 : C2, U: C3](x: T)(using y: U, z: V): R
22+
def f[T <: B : C](x: T): R = ...
2423
```
2524

26-
would expand to
25+
## Named Context Bounds
2726

27+
A context bound can be given a name with an `as` clause. For example, assume the following trait definitions.
28+
```scala
29+
trait SemiGroup[A]:
30+
extension (x: A) def combine(y: A): A
31+
32+
trait Monoid[A] extends SemiGroup[A]:
33+
def unit: A
34+
```
35+
We can write `reduce` function over lists of monoid instances like this:
2836
```scala
29-
def f[T, U](x: T)(using _: C1[T], _: C2[T], _: C3[U], y: U, z: V): R
37+
def reduce[A: Monoid as m](xs: List[A]): A =
38+
xs.foldLeft(m.unit)(_ `combine` _)
3039
```
40+
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.
3141

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+
def reduce[A](xs: List[A])(using m: 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
3355

56+
A type parameter can have several context bounds. If there are multiple bounds, they are written inside braces `{...}`. Example:
3457
```scala
35-
def g[T <: B : C](x: T): R = ...
58+
trait:
59+
def showMax[X : {Ord, Show}](x: X, y: X): String
60+
class B extends A:
61+
def showMax[X : {Ord as ordering, Show as show}](x: X, y: X): String =
62+
show.asString(ordering.max(x, y))
3663
```
3764

38-
## Migration
65+
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.
3967

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
4269

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.
77+
78+
**Examples:**
79+
80+
1. By rule 3,
81+
```scala
82+
def f[T: {C1, C2}](x: T): R
83+
```
84+
expands to
85+
```scala
86+
def f[T](x: T)(using C1, C2): R
87+
```
88+
Equally by rule 3,
89+
```scala
90+
def f[T: {C1 as c1, C2 as c2}](x: T): R
91+
```
92+
expands to
93+
```scala
94+
def f[T](x: T)(using c1: C1, c2: C2): R
95+
96+
2. By rule 2,
97+
```scala
98+
def f[T: {C1, C2}, U: C3](x: T)(using y: U, z: V): R
99+
```
100+
expands to
101+
```scala
102+
def f[T, U](x: T)(using _: C1[T], _: C2[T], _: C3[U], y: U, z: V): R
103+
```
104+
The same expansion occurs if `y` and `z` are Scala 2 style `implicit` parameters.
105+
3. Assume the following trait definition:
106+
```scala
107+
trait Parser[P]:
108+
type Input
109+
type Result
110+
```
111+
Here is a method `run` that runs a parser on an input of the required type:
112+
```scala
113+
def run[P : Parser as p](in: p.Input): p.Result
114+
```
115+
By rule 1, this method definition is expanded to:
116+
```scala
117+
def run[P](using p: Parser[P]](in: p.Input): p.Result
118+
```
119+
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 Scala 3.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. From Scala 3.6 on, they will map to context parameters instead, as is described above.
126+
127+
If the source version is `3.6-migration`, any pairing of an evidence
44128
context parameter stemming from a context bound with a normal argument will give a migration
45129
warning. The warning indicates that a `(using ...)` clause is needed instead. The rewrite can be
46130
done automatically under `-rewrite`.
47131

132+
## Context Bounds for Polymorphic Functions
133+
134+
From Scala 3.6 on, context bounds can also be used in polymorphic function types and polymorphic function literals:
135+
136+
```scala
137+
type Comparer = [X: Ord] => (x: X, y: X) => Boolean
138+
val less: Comparer = [X: Ord as ord] => (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
145+
```scala
146+
type Comparer = [X] => (x: X, y: X) => Ord[X] ?=> Boolean
147+
val less: Comparer = [X] => (x: X, y: X) => (ord: Ord[X]) ?=>
148+
ord.compare(x, y) < 0
149+
```
150+
151+
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+
type Cmp[X] = (x: X, y: X) => Ord[X] ?=> Boolean
155+
type Comparer2 = [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+
class Collection:
168+
type Element: 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+
class Collection:
177+
type Element
178+
given Ord[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.
181+
182+
48183
## Syntax
49184

185+
The new syntax of context bounds is as follows:
186+
50187
```ebnf
51-
TypeParamBounds ::= [SubtypeBounds] {ContextBound}
52-
ContextBound ::= ‘:’ Type
188+
TypeParamBounds ::= TypeAndCtxBounds
189+
TypeAndCtxBounds ::= TypeBounds [‘:’ ContextBounds]
190+
ContextBounds ::= ContextBound
191+
| '{' ContextBound {',' ContextBound} '}'
192+
ContextBound ::= Type ['as' id]
53193
```
194+
195+
The syntax of function types and function literals
196+
is generalized as follows to allow context bounds for generic type parameters.
197+
198+
```ebnf
199+
FunType ::= FunTypeArgs (‘=>’ | ‘?=>’) Type
200+
| DefTypeParamClause '=>' Type
201+
FunExpr ::= FunParams (‘=>’ | ‘?=>’) Expr
202+
| DefTypeParamClause ‘=>’ Expr
203+
```
204+
The syntax for abstract type members is generalized as follows to allow context bounds:
205+
206+
```scala
207+
TypeDef ::= id [TypeParamClause] TypeAndCtxBounds
208+
```

docs/_docs/reference/contextual/conversions.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ abstract class Conversion[-T, +U] extends (T => U):
1212
```
1313
For example, here is an implicit conversion from `String` to `Token`:
1414
```scala
15-
given Conversion[String, Token] with
15+
given Conversion[String, Token]:
1616
def apply(str: String): Token = new KeyWord(str)
1717
```
1818
Using an alias this can be expressed more concisely as:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
---
2+
layout: doc-page
3+
title: "Deferred Givens"
4+
nightlyOf: https://docs.scala-lang.org/scala3/reference/contextual/deferred-givens.html
5+
---
6+
7+
Scala 3.6 introduces a new way to implement a given definition in a trait like this:
8+
```scala
9+
given T = 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+
trait Sorted:
17+
type Element : Ord
18+
19+
class SortedSet[A : Ord as ord] extends Sorted:
20+
type Element = A
21+
```
22+
The compiler expands this to the following implementation.
23+
```scala
24+
trait Sorted:
25+
type Element
26+
given Ord[Element] = compiletime.deferred
27+
28+
class SortedSet[A](using ord: Ord[A]) extends Sorted:
29+
type Element = A
30+
override given Ord[Element] = ord
31+
```
32+
33+
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+
class SortedString[A] extends Sorted:
39+
type Element = String
40+
override given Ord[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+
trait HasOrd[T]:
51+
given ord: 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.

docs/_docs/reference/contextual/derivation-macro.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -135,10 +135,10 @@ trait Eq[T]:
135135
def eqv(x: T, y: T): Boolean
136136

137137
object Eq:
138-
given Eq[String] with
138+
given Eq[String]:
139139
def eqv(x: String, y: String) = x == y
140140

141-
given Eq[Int] with
141+
given Eq[Int]:
142142
def eqv(x: Int, y: Int) = x == y
143143

144144
def eqProduct[T](body: (T, T) => Boolean): Eq[T] =

0 commit comments

Comments
 (0)