Skip to content

Commit 72c84f7

Browse files
authored
Merge pull request #6413 from dotty-staging/try-contextual-delegate
Fixes to delegate and implicits trials
2 parents 9fddb84 + 48adb96 commit 72c84f7

20 files changed

+178
-180
lines changed

docs/docs/reference/contextual-delegate/derivation.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,16 @@ enum Tree[T] derives Eql, Ordering, Pickling {
1010
case Leaf(elem: T)
1111
}
1212
```
13-
The `derives` clause generates delegate delegates for the `Eql`, `Ordering`, and `Pickling` traits in the companion object `Tree`:
13+
The `derives` clause generates delegates for the `Eql`, `Ordering`, and `Pickling` traits in the companion object `Tree`:
1414
```scala
15-
delegate [T: Eql] for Eql[Tree[T]] = Eql.derived
15+
delegate [T: Eql] for Eql[Tree[T]] = Eql.derived
1616
delegate [T: Ordering] for Ordering[Tree[T]] = Ordering.derived
1717
delegate [T: Pickling] for Pickling[Tree[T]] = Pickling.derived
1818
```
1919

2020
### Deriving Types
2121

22-
Besides for `enums`, typeclasses can also be derived for other sets of classes and objects that form an algebraic data type. These are:
22+
Besides for enums, typeclasses can also be derived for other sets of classes and objects that form an algebraic data type. These are:
2323

2424
- individual case classes or case objects
2525
- sealed classes or traits that have only case classes and case objects as children.
@@ -234,7 +234,7 @@ there exists evidence of type `Generic[T]`. Here's a possible solution:
234234
The implementation of the inline method `derived` creates a delegate for `Eql[T]` and implements its `eql` method. The right-hand side of `eql` mixes compile-time and runtime elements. In the code above, runtime elements are marked with a number in parentheses, i.e
235235
`(1)`, `(2)`, `(3)`. Compile-time calls that expand to runtime code are marked with a number in brackets, i.e. `[4]`, `[5]`. The implementation of `eql` consists of the following steps.
236236

237-
1. Map the compared values `x` and `y` to their mirrors using the `reflect` method of the implicitly passed `Generic` evidence `(1)`, `(2)`.
237+
1. Map the compared values `x` and `y` to their mirrors using the `reflect` method of the implicitly passed `Generic` `(1)`, `(2)`.
238238
2. Match at compile-time against the shape of the ADT given in `ev.Shape`. Dotty does not have a construct for matching types directly, but we can emulate it using an `inline` match over an `erasedValue`. Depending on the actual type `ev.Shape`, the match will reduce at compile time to one of its two alternatives.
239239
3. If `ev.Shape` is of the form `Cases[alts]` for some tuple `alts` of alternative types, the equality test consists of comparing the ordinal values of the two mirrors `(3)` and, if they are equal, comparing the elements of the case indicated by that ordinal value. That second step is performed by code that results from the compile-time expansion of the `eqlCases` call `[4]`.
240240
4. If `ev.Shape` is of the form `Case[elems]` for some tuple `elems` for element types, the elements of the case are compared by code that results from the compile-time expansion of the `eqlElems` call `[5]`.

docs/docs/reference/contextual-delegate/inferable-params.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ The `max` method can be applied as follows:
2020
```scala
2121
max(2, 3).given(IntOrd)
2222
```
23-
The `.given(IntOrd)` part provides the `IntOrd` delegate as an argument for the `ord` parameter. But the point of
23+
The `.given(IntOrd)` part passes `IntOrd` as an argument for the `ord` parameter. But the point of
2424
implicit parameters is that this argument can also be left out (and it usually is). So the following
2525
applications are equally valid:
2626
```scala

docs/docs/reference/contextual-delegate/instance-defs.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,11 @@ the type(s) in the `for` clause.
5050

5151
An alias can be used to define a delegate that is equal to some expression. E.g.:
5252
```scala
53-
delegate ctx for ExecutionContext = new ForkJoinPool()
53+
delegate global for ExecutionContext = new ForkJoinPool()
5454
```
55-
This creates a delegate `ctx` of type `ExecutionContext` that resolves to the right hand side `new ForkJoinPool()`.
56-
The first time a delegate for `ExecutionContext` is demanded, a new `ForkJoinPool` is created, which is then
57-
returned for this and all subsequent accesses to `ctx`.
55+
This creates a delegate `global` of type `ExecutionContext` that resolves to the right hand side `new ForkJoinPool()`.
56+
The first time `global` is accessed, a new `ForkJoinPool` is created, which is then
57+
returned for this and all subsequent accesses to `global`.
5858

5959
Alias delegates can be anonymous, e.g.
6060
```scala
@@ -64,7 +64,7 @@ An alias delegate can have type and context parameters just like any other deleg
6464

6565
## Delegate Creation
6666

67-
A delegate without type parameters or given clause is created on-demand, the first time it is accessed. No attempt is made to ensure safe publication, which means that different threads might create different delegates for the same `delegate` clause. If a `delegate` clause has type parameters or a given clause, a fresh delegate is created for each reference.
67+
A delegate without type parameters or given clause is created on-demand, the first time it is accessed. It is not required to ensure safe publication, which means that different threads might create different delegates for the same `delegate` clause. If a `delegate` clause has type parameters or a given clause, a fresh delegate is created for each reference.
6868

6969
## Syntax
7070

docs/docs/reference/contextual-delegate/multiversal-equality.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ delegate [T, U] for Eql[Box[T], Box[U]] given Eql[T, U] = Eql.derived
103103
```
104104
That is, two boxes are comparable with `==` or `!=` if their elements are. Examples:
105105
```scala
106-
new Box(1) == new Box(1L) // ok since `Eql[Int, Long]` is represented.
106+
new Box(1) == new Box(1L) // ok since there is a delegate for `Eql[Int, Long]`
107107
new Box(1) == new Box("a") // error: can't compare
108108
new Box(1) == 1 // error: can't compare
109109
```
@@ -116,7 +116,7 @@ If the `strictEquality` feature is enabled then
116116
a comparison using `x == y` or `x != y` between values `x: T` and `y: U`
117117
is legal if
118118

119-
1. there is representation of type `Eql[T, U]`, or
119+
1. there is a delegate for `Eql[T, U]`, or
120120
2. one of `T`, `U` is `Null`.
121121

122122
In the default case where the `strictEquality` feature is not enabled the comparison is
@@ -136,7 +136,7 @@ Explanations:
136136

137137
## Predefined Eql Delegates
138138

139-
The `Eql` object defines delegates for
139+
The `Eql` object defines delegates for comparing
140140
- the primitive types `Byte`, `Short`, `Char`, `Int`, `Long`, `Float`, `Double`, `Boolean`, and `Unit`,
141141
- `java.lang.Number`, `java.lang.Boolean`, and `java.lang.Character`,
142142
- `scala.collection.Seq`, and `scala.collection.Set`.

docs/docs/reference/contextual-delegate/query-types.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ where the names `x_1`, ..., `x_n` are arbitrary. This expansion is performed
2828
before the expression `E` is typechecked, which means that `x_1`, ..., `x_n`
2929
are available as delegates in `E`.
3030

31-
Like their types, implicit function iterals are written with a `given` prefix. They differ from normal function literals in two ways:
31+
Like their types, implicit function literals are written with a `given` prefix. They differ from normal function literals in two ways:
3232

3333
1. Their parameters are implicit.
3434
2. Their types are implicit function types.

docs/docs/reference/contextual-delegate/relationship-implicits.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,17 @@ Delegate clauses can be mapped to combinations of implicit objects, classes and
2828
class ListOrd[T](implicit ord: Ord[T]) extends Ord[List[T]] { ... }
2929
final implicit def ListOrd[T](implicit ord: Ord[T]): ListOrd[T] = new ListOrd[T]
3030
```
31-
3. Alias delegates map to implicit methods. If the delegates has neither type parameters nor a given clause, the result of creating an instance is cached in a variable. If in addition the right hand side is pure and cheap to compute, a simple `val` can be used instead. E.g.,
31+
3. Alias delegates map to implicit methods. If the delegate has neither type parameters nor a given clause, the result of creating an instance is cached in a variable. If in addition the right hand side is pure and cheap to compute, a simple `val` can be used instead. E.g.,
3232
```scala
33-
delegate ec for ExecutionContext = new ForkJoinContext()
33+
delegate global for ExecutionContext = new ForkJoinContext()
3434
delegate config for Config = default.config
3535
```
3636
map to
3737
```scala
38-
private[this] var ec$cache: ExecutionContext | Null = null
39-
final implicit def ec: ExecutionContext = {
40-
if (ec$cache == null) ec$cache = new ForkJoinContext()
41-
ec$cache
38+
private[this] var global$cache: ExecutionContext | Null = null
39+
final implicit def global: ExecutionContext = {
40+
if (global$cache == null) global$cache = new ForkJoinContext()
41+
global$cache
4242
}
4343

4444
final implicit val config: Config = default.config

docs/docs/reference/contextual-delegate/typeclasses.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ layout: doc-page
33
title: "Implementing Typeclasses"
44
---
55

6-
Traits, delegates, extension methods and context bounds
6+
Delegates, extension methods and context bounds
77
allow a concise and natural expression of _typeclasses_. Typeclasses are just traits
88
with canonical implementations defined by delegates. Here are some examples of standard typeclasses:
99

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ title: "Context Bounds"
55

66
## Context Bounds
77

8-
A context bound is a shorthand for expressing a common pattern of an inferable parameter that depends on a type parameter. Using a context bound, the `maximum` function of the last section can be written like this:
8+
A context bound is a shorthand for expressing a common pattern of an implicit parameter that depends on a type parameter. Using a context bound, the `maximum` function of the last section can be written like this:
99
```scala
1010
def maximum[T: Ord](xs: List[T]): T = xs.reduceLeft(max)
1111
```
12-
A bound like `: Ord` on a type parameter `T` of a method or class indicates an inferable parameter `given Ord[T]`. The inferable parameter(s) generated from context bounds come last in the definition of the containing method or class. E.g.,
12+
A bound like `: Ord` on a type parameter `T` of a method or class is equivalent to a given clause `given Ord[T]`. The implicit parameter(s) generated from context bounds come last in the definition of the containing method or class. E.g.,
1313
```scala
1414
def f[T: C1 : C2, U: C3](x: T) given (y: U, z: V): R
1515
```

docs/docs/reference/contextual-implicit/conversions.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ layout: doc-page
33
title: "Implicit Conversions"
44
---
55

6-
Implicit conversions are defined by implicits for the `scala.Conversion` class.
6+
Implicit conversions are defined by implicit instances of the `scala.Conversion` class.
77
This class is defined in package `scala` as follows:
88
```scala
99
abstract class Conversion[-T, +U] extends (T => U)
@@ -14,7 +14,7 @@ implicit for Conversion[String, Token] {
1414
def apply(str: String): Token = new KeyWord(str)
1515
}
1616
```
17-
Using an implicit alias this can be expressed more concisely as:
17+
Using an alias implicit this can be expressed more concisely as:
1818
```scala
1919
implicit for Conversion[String, Token] = new KeyWord(_)
2020
```
@@ -27,9 +27,9 @@ An implicit conversion is applied automatically by the compiler in three situati
2727

2828
In the first case, the compiler looks for an implicit value of class
2929
`scala.Conversion` that maps an argument of type `T` to type `S`. In the second and third
30-
case, it looks for an evidance value of class `scala.Conversion` that maps an argument of type `T`
30+
case, it looks for an implicit value of class `scala.Conversion` that maps an argument of type `T`
3131
to a type that defines a member `m` which can be applied to `args` if present.
32-
If such an instance `C` is found, the expression `e` is replaced by `C.apply(e)`.
32+
If such a value `C` is found, the expression `e` is replaced by `C.apply(e)`.
3333

3434
## Examples
3535

docs/docs/reference/contextual-implicit/derivation.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ implicit [T: Pickling] for Pickling[Tree[T]] = Pickling.derived
1919

2020
### Deriving Types
2121

22-
Besides for `enums`, typeclasses can also be derived for other sets of classes and objects that form an algebraic data type. These are:
22+
Besides for enums, typeclasses can also be derived for other sets of classes and objects that form an algebraic data type. These are:
2323

2424
- individual case classes or case objects
2525
- sealed classes or traits that have only case classes and case objects as children.
@@ -42,7 +42,7 @@ A trait or class can appear in a `derives` clause if its companion object define
4242
```scala
4343
def derived[T] given Generic[T] = ...
4444
```
45-
That is, the `derived` method takes an inferable parameter of type `Generic` that determines the _shape_ of the deriving type `T` and it computes the typeclass implementation according to that shape. Implicits for `Generic` are generated automatically for any type that derives a typeclass with a `derived`
45+
That is, the `derived` method takes an implicit parameter of type `Generic` that determines the _shape_ of the deriving type `T` and it computes the typeclass implementation according to that shape. Implicits for `Generic` are generated automatically for any type that derives a typeclass with a `derived`
4646
method that refers to `Generic`. One can also derive `Generic` alone, which means a `Generic` instance is generated without any other type class instances. E.g.:
4747
```scala
4848
sealed trait ParseResult[T] derives Generic
@@ -141,15 +141,15 @@ abstract class Generic[T] {
141141
```
142142
It defines the `Shape` type for the ADT `T`, as well as two methods that map between a
143143
type `T` and a generic representation of `T`, which we call a `Mirror`:
144-
The `reflect` method maps an instance value of the ADT `T` to its mirror whereas
144+
The `reflect` method maps an instance of the ADT `T` to its mirror whereas
145145
the `reify` method goes the other way. There's also a `common` method that returns
146146
a value of type `GenericClass` which contains information that is the same for all
147147
instances of a class (right now, this consists of the runtime `Class` value and
148148
the names of the cases and their parameters).
149149

150150
### Mirrors
151151

152-
A mirror is a generic representation of an instance value of an ADT. `Mirror` objects have three components:
152+
A mirror is a generic representation of an instance of an ADT. `Mirror` objects have three components:
153153

154154
- `adtClass: GenericClass`: The representation of the ADT class
155155
- `ordinal: Int`: The ordinal number of the case among all cases of the ADT, starting from 0
@@ -214,8 +214,8 @@ trait Eql[T] {
214214
def eql(x: T, y: T): Boolean
215215
}
216216
```
217-
We need to implement a method `Eql.derived` that produces an instance of `Eql[T]` provided
218-
there exists an implicit of type `Generic[T]`. Here's a possible solution:
217+
We need to implement a method `Eql.derived` that produces an implicit value of type `Eql[T]` provided
218+
there exists an implicit value of type `Generic[T]`. Here's a possible solution:
219219
```scala
220220
inline def derived[T] given (ev: Generic[T]): Eql[T] = new Eql[T] {
221221
def eql(x: T, y: T): Boolean = {
@@ -314,7 +314,7 @@ calling the `error` method defined in `scala.compiletime`.
314314
**Example:** Here is a slightly polished and compacted version of the code that's generated by inline expansion for the derived `Eql` instance of class `Tree`.
315315

316316
```scala
317-
implicit [T] given (elemEq: Eql[T]) for Eql[Tree[T]] {
317+
implicit [T] for Eql[Tree[T]] given (elemEq: Eql[T]) {
318318
def eql(x: Tree[T], y: Tree[T]): Boolean = {
319319
val ev = the[Generic[Tree[T]]]
320320
val mx = ev.reflect(x)
@@ -342,7 +342,7 @@ To do this, simply define an instance with the `derived` method of the typeclass
342342
```scala
343343
implicit [T: Ordering]: Ordering[Option[T]] = Ordering.derived
344344
```
345-
Usually, the `Ordering.derived` clause has an inferable parameter of type
345+
Usually, the `Ordering.derived` clause has an implicit parameter of type
346346
`Generic[Option[T]]`. Since the `Option` trait has a `derives` clause,
347347
the necessary implicit is already present in the companion object of `Option`.
348348
If the ADT in question does not have a `derives` clause, an implicit for `Generic`

docs/docs/reference/contextual-implicit/extension-methods.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ So `circle.circumference` translates to `CircleOps.circumference(circle)`, provi
8080

8181
### Implicits for Extension Methods
8282

83-
An implicit instance that defines extension methods can also be defined without a `for` clause. E.g.,
83+
An implicit instance that define extension methods can also be defined without a `for` clause. E.g.,
8484

8585
```scala
8686
implicit StringOps {

docs/docs/reference/contextual-implicit/import-implied.md

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,22 +28,17 @@ There are two main benefits arising from these rules:
2828
values can be anonymous, so the usual recourse of using named imports is not
2929
practical.
3030

31-
### Relationship with Old-Style Implicits
31+
### Migration
3232

33-
The rules of "import implicit" above have the consequence that a library
34-
would have to migrate in lockstep with all its users from old style implicit definitions and
35-
normal imports to new style implicit definitions and `import implicit`.
33+
The rules as stated above would break all existing code that imports implicits, which is of course unacceptable.
34+
To make gradual migration possible, we adapt the following scheme.
3635

37-
The following modifications avoid this hurdle to migration.
36+
1. In Scala 3.0, a normal import will also import implicits written in the old "implicit-as-a-modifier" style.
37+
So these implicits can be brought into scope using either a normal import or an `import implicit`.
3838

39-
1. An `import implicit` also brings old style implicits into scope. So, in Scala 3.0
40-
an old-style implicit definition can be brought into scope either by a normal import
41-
or by an `import implicit`.
39+
2. In Scala 3.1, an old-style implicit accessed implicitly through a normal import will give a deprecation warning.
4240

43-
2. In Scala 3.1, an old-style implicits accessed implicitly through a normal import
44-
will give a deprecation warning.
45-
46-
3. In some version after 3.1, an old-style implicits accessed implicitly through a normal import
41+
3. In some version after 3.1, an old-style implicit accessed implicitly through a normal import
4742
will give a compiler error.
4843

4944
These rules mean that library users can use `import implicit` to access old-style implicits in Scala 3.0,

docs/docs/reference/contextual-implicit/inferable-by-name-parameters.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
---
22
layout: doc-page
3-
title: "Inferable By-Name Parameters"
3+
title: "Implicit By-Name Parameters"
44
---
55

6-
Inferable by-name parameters can be used to avoid a divergent inferred expansion. Example:
6+
Implicit by-name parameters can be used to avoid a divergent inferred expansion. Example:
77

88
```scala
99
trait Codec[T] {
@@ -12,7 +12,7 @@ trait Codec[T] {
1212

1313
implicit intCodec for Codec[Int] = ???
1414

15-
implicit optionCodec[T] given (ev: => Codec[T]) for Codec[Option[T]] {
15+
implicit optionCodec[T] for Codec[Option[T]] given (ev: => Codec[T]) {
1616
def write(xo: Option[T]) = xo match {
1717
case Some(x) => ev.write(x)
1818
case None =>
@@ -24,23 +24,23 @@ val s = the[Codec[Option[Int]]]
2424
s.write(Some(33))
2525
s.write(None)
2626
```
27-
As is the case for a normal by-name parameter, the argument for the inferable parameter `ev`
27+
As is the case for a normal by-name parameter, the argument for the implicit parameter `ev`
2828
is evaluated on demand. In the example above, if the option value `x` is `None`, it is
2929
not evaluated at all.
3030

31-
The synthesized argument for an inferable parameter is backed by a local val
31+
The synthesized argument for an implicit parameter is backed by a local val
3232
if this is necessary to prevent an otherwise diverging expansion.
3333

34-
The precise steps for constructing an inferable argument for a by-name parameter of type `=> T` are as follows.
34+
The precise steps for synthesizing an argument for a by-name parameter of type `=> T` are as follows.
3535

36-
1. Create a new implicit value of type `T`:
36+
1. Create a new implicit for type `T`:
3737

3838
```scala
3939
implicit lv for T = ???
4040
```
4141
where `lv` is an arbitrary fresh name.
4242

43-
1. This instance is not immediately available as candidate for argument inference (making it immediately available could result in a loop in the synthesized computation). But it becomes available in all nested contexts that look again for an inferred argument to a by-name parameter.
43+
1. This implicit is not immediately available as candidate for argument inference (making it immediately available could result in a loop in the synthesized computation). But it becomes available in all nested contexts that look again for an implicit argument to a by-name parameter.
4444

4545
1. If this search succeeds with expression `E`, and `E` contains references to the implicit `lv`, replace `E` by
4646

@@ -58,7 +58,7 @@ val s = the[Test.Codec[Option[Int]]](
5858
optionCodec[Int](intCodec))
5959
```
6060

61-
No local instance was generated because the synthesized argument is not recursive.
61+
No local implicit was generated because the synthesized argument is not recursive.
6262

6363
### Reference
6464

0 commit comments

Comments
 (0)