Skip to content

Fixes to delegate and implicits trials #6413

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 1, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions docs/docs/reference/contextual-delegate/derivation.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,16 @@ enum Tree[T] derives Eql, Ordering, Pickling {
case Leaf(elem: T)
}
```
The `derives` clause generates delegate delegates for the `Eql`, `Ordering`, and `Pickling` traits in the companion object `Tree`:
The `derives` clause generates delegates for the `Eql`, `Ordering`, and `Pickling` traits in the companion object `Tree`:
```scala
delegate [T: Eql] for Eql[Tree[T]] = Eql.derived
delegate [T: Eql] for Eql[Tree[T]] = Eql.derived
delegate [T: Ordering] for Ordering[Tree[T]] = Ordering.derived
delegate [T: Pickling] for Pickling[Tree[T]] = Pickling.derived
```

### Deriving Types

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

- individual case classes or case objects
- sealed classes or traits that have only case classes and case objects as children.
Expand Down Expand Up @@ -234,7 +234,7 @@ there exists evidence of type `Generic[T]`. Here's a possible solution:
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
`(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.

1. Map the compared values `x` and `y` to their mirrors using the `reflect` method of the implicitly passed `Generic` evidence `(1)`, `(2)`.
1. Map the compared values `x` and `y` to their mirrors using the `reflect` method of the implicitly passed `Generic` `(1)`, `(2)`.
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.
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]`.
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]`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ The `max` method can be applied as follows:
```scala
max(2, 3).given(IntOrd)
```
The `.given(IntOrd)` part provides the `IntOrd` delegate as an argument for the `ord` parameter. But the point of
The `.given(IntOrd)` part passes `IntOrd` as an argument for the `ord` parameter. But the point of
implicit parameters is that this argument can also be left out (and it usually is). So the following
applications are equally valid:
```scala
Expand Down
10 changes: 5 additions & 5 deletions docs/docs/reference/contextual-delegate/instance-defs.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,11 @@ the type(s) in the `for` clause.

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

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

## Delegate Creation

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

## Syntax

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ delegate [T, U] for Eql[Box[T], Box[U]] given Eql[T, U] = Eql.derived
```
That is, two boxes are comparable with `==` or `!=` if their elements are. Examples:
```scala
new Box(1) == new Box(1L) // ok since `Eql[Int, Long]` is represented.
new Box(1) == new Box(1L) // ok since there is a delegate for `Eql[Int, Long]`
new Box(1) == new Box("a") // error: can't compare
new Box(1) == 1 // error: can't compare
```
Expand All @@ -116,7 +116,7 @@ If the `strictEquality` feature is enabled then
a comparison using `x == y` or `x != y` between values `x: T` and `y: U`
is legal if

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

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

## Predefined Eql Delegates

The `Eql` object defines delegates for
The `Eql` object defines delegates for comparing
- the primitive types `Byte`, `Short`, `Char`, `Int`, `Long`, `Float`, `Double`, `Boolean`, and `Unit`,
- `java.lang.Number`, `java.lang.Boolean`, and `java.lang.Character`,
- `scala.collection.Seq`, and `scala.collection.Set`.
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/reference/contextual-delegate/query-types.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ where the names `x_1`, ..., `x_n` are arbitrary. This expansion is performed
before the expression `E` is typechecked, which means that `x_1`, ..., `x_n`
are available as delegates in `E`.

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

1. Their parameters are implicit.
2. Their types are implicit function types.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,17 @@ Delegate clauses can be mapped to combinations of implicit objects, classes and
class ListOrd[T](implicit ord: Ord[T]) extends Ord[List[T]] { ... }
final implicit def ListOrd[T](implicit ord: Ord[T]): ListOrd[T] = new ListOrd[T]
```
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.,
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.,
```scala
delegate ec for ExecutionContext = new ForkJoinContext()
delegate global for ExecutionContext = new ForkJoinContext()
delegate config for Config = default.config
```
map to
```scala
private[this] var ec$cache: ExecutionContext | Null = null
final implicit def ec: ExecutionContext = {
if (ec$cache == null) ec$cache = new ForkJoinContext()
ec$cache
private[this] var global$cache: ExecutionContext | Null = null
final implicit def global: ExecutionContext = {
if (global$cache == null) global$cache = new ForkJoinContext()
global$cache
}

final implicit val config: Config = default.config
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/reference/contextual-delegate/typeclasses.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ layout: doc-page
title: "Implementing Typeclasses"
---

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

Expand Down
4 changes: 2 additions & 2 deletions docs/docs/reference/contextual-implicit/context-bounds.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ title: "Context Bounds"

## Context Bounds

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:
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:
```scala
def maximum[T: Ord](xs: List[T]): T = xs.reduceLeft(max)
```
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.,
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.,
```scala
def f[T: C1 : C2, U: C3](x: T) given (y: U, z: V): R
```
Expand Down
8 changes: 4 additions & 4 deletions docs/docs/reference/contextual-implicit/conversions.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ layout: doc-page
title: "Implicit Conversions"
---

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

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

## Examples

Expand Down
16 changes: 8 additions & 8 deletions docs/docs/reference/contextual-implicit/derivation.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ implicit [T: Pickling] for Pickling[Tree[T]] = Pickling.derived

### Deriving Types

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

- individual case classes or case objects
- sealed classes or traits that have only case classes and case objects as children.
Expand All @@ -42,7 +42,7 @@ A trait or class can appear in a `derives` clause if its companion object define
```scala
def derived[T] given Generic[T] = ...
```
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`
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`
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.:
```scala
sealed trait ParseResult[T] derives Generic
Expand Down Expand Up @@ -141,15 +141,15 @@ abstract class Generic[T] {
```
It defines the `Shape` type for the ADT `T`, as well as two methods that map between a
type `T` and a generic representation of `T`, which we call a `Mirror`:
The `reflect` method maps an instance value of the ADT `T` to its mirror whereas
The `reflect` method maps an instance of the ADT `T` to its mirror whereas
the `reify` method goes the other way. There's also a `common` method that returns
a value of type `GenericClass` which contains information that is the same for all
instances of a class (right now, this consists of the runtime `Class` value and
the names of the cases and their parameters).

### Mirrors

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

- `adtClass: GenericClass`: The representation of the ADT class
- `ordinal: Int`: The ordinal number of the case among all cases of the ADT, starting from 0
Expand Down Expand Up @@ -214,8 +214,8 @@ trait Eql[T] {
def eql(x: T, y: T): Boolean
}
```
We need to implement a method `Eql.derived` that produces an instance of `Eql[T]` provided
there exists an implicit of type `Generic[T]`. Here's a possible solution:
We need to implement a method `Eql.derived` that produces an implicit value of type `Eql[T]` provided
there exists an implicit value of type `Generic[T]`. Here's a possible solution:
```scala
inline def derived[T] given (ev: Generic[T]): Eql[T] = new Eql[T] {
def eql(x: T, y: T): Boolean = {
Expand Down Expand Up @@ -314,7 +314,7 @@ calling the `error` method defined in `scala.compiletime`.
**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`.

```scala
implicit [T] given (elemEq: Eql[T]) for Eql[Tree[T]] {
implicit [T] for Eql[Tree[T]] given (elemEq: Eql[T]) {
def eql(x: Tree[T], y: Tree[T]): Boolean = {
val ev = the[Generic[Tree[T]]]
val mx = ev.reflect(x)
Expand Down Expand Up @@ -342,7 +342,7 @@ To do this, simply define an instance with the `derived` method of the typeclass
```scala
implicit [T: Ordering]: Ordering[Option[T]] = Ordering.derived
```
Usually, the `Ordering.derived` clause has an inferable parameter of type
Usually, the `Ordering.derived` clause has an implicit parameter of type
`Generic[Option[T]]`. Since the `Option` trait has a `derives` clause,
the necessary implicit is already present in the companion object of `Option`.
If the ADT in question does not have a `derives` clause, an implicit for `Generic`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ So `circle.circumference` translates to `CircleOps.circumference(circle)`, provi

### Implicits for Extension Methods

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

```scala
implicit StringOps {
Expand Down
19 changes: 7 additions & 12 deletions docs/docs/reference/contextual-implicit/import-implied.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,22 +28,17 @@ There are two main benefits arising from these rules:
values can be anonymous, so the usual recourse of using named imports is not
practical.

### Relationship with Old-Style Implicits
### Migration

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

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

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

2. In Scala 3.1, an old-style implicits accessed implicitly through a normal import
will give a deprecation warning.

3. In some version after 3.1, an old-style implicits accessed implicitly through a normal import
3. In some version after 3.1, an old-style implicit accessed implicitly through a normal import
will give a compiler error.

These rules mean that library users can use `import implicit` to access old-style implicits in Scala 3.0,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
---
layout: doc-page
title: "Inferable By-Name Parameters"
title: "Implicit By-Name Parameters"
---

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

```scala
trait Codec[T] {
Expand All @@ -12,7 +12,7 @@ trait Codec[T] {

implicit intCodec for Codec[Int] = ???

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

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

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

1. Create a new implicit value of type `T`:
1. Create a new implicit for type `T`:

```scala
implicit lv for T = ???
```
where `lv` is an arbitrary fresh name.

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

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

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

No local instance was generated because the synthesized argument is not recursive.
No local implicit was generated because the synthesized argument is not recursive.

### Reference

Expand Down
Loading