Skip to content

Fixes to repr and implicit trials #6417

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 2, 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
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ The precise steps for synthesizing an argument for a by-name parameter of type `
```
where `lv` is an arbitrary fresh name.

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. This implicit is not immediately available as a 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 Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ max(List(1, 2, 3), Nil)

## Anonymous Implicit Parameters

In many situations, the name of an implicit parameter of a method need not be mentioned explicitly at all, since it is only used as synthesized implicit for other constraints. In that case one can avoid defining a parameter name and just provide its type. Example:
In many situations, the name of an implicit parameter of a method need not be mentioned explicitly at all, since it is only used as a synthesized argument for other implicit parameters. In that case one can avoid defining a parameter name and just provide its type. Example:
```scala
def maximum[T](xs: List[T]) given Ord[T]: T =
xs.reduceLeft(max)
Expand Down
4 changes: 2 additions & 2 deletions docs/docs/reference/contextual-implicit/instance-defs.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,14 @@ The name of an implicit instance can be left out. So the implicit instance defin
of the last section can also be expressed like this:
```scala
implicit for Ord[Int] { ... }
implicit [T] or Ord[List[T]] given (ord: Ord[T]) { ... }
implicit [T] for Ord[List[T]] given (ord: Ord[T]) { ... }
```
If the name of an implicit is missing, the compiler will synthesize a name from
the type(s) in the `for` clause.

## Alias Implicits

An alias implicit defines an implicit value that is equal to some expression. E.g.:
An alias can be used to define an implicit value that is equal to some expression. E.g.:
```scala
implicit global for ExecutionContext = new ForkJoinPool()
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ that derives `Eql`, e.g.
```scala
class T derives Eql
```
Alternatively, one can also provide an `Eql` implicit directly, like this:
Alternatively, one can also define an `Eql` implicit directly, like this:
```scala
implicit for Eql[T, T] = Eql.derived
```
Expand Down Expand Up @@ -141,7 +141,7 @@ The `Eql` object defines implicits for comparing
- `java.lang.Number`, `java.lang.Boolean`, and `java.lang.Character`,
- `scala.collection.Seq`, and `scala.collection.Set`.

Implicits are defined so that every one of these types is has a reflexive `Eql` implicit, and the following holds:
Implicits are defined so that every one of these types has a reflexive `Eql` implicit, and the following holds:

- Primitive numeric types can be compared with each other.
- Primitive numeric types can be compared with subtypes of `java.lang.Number` (and _vice versa_).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,4 @@ Gist](https://gist.github.com/OlivierBlanvillain/234d3927fe9e9c6fba074b53a7bd9

### Type Checking

After desugaring no additional typing rules are required for context query types.
After desugaring no additional typing rules are required for implicit function types.
Original file line number Diff line number Diff line change
Expand Up @@ -140,11 +140,11 @@ one can write

### Implicit Classes

Implicit classes in Scala 2 are often used to define extension methods, which are directly supported in Dotty. Other uses of implicit classes can be simulated by a pair of a regular class and a conversion delegate.
Implicit classes in Scala 2 are often used to define extension methods, which are directly supported in Dotty. Other uses of implicit classes can be simulated by a pair of a regular class and a conversion instance.

### Abstract Implicits

An abstract implicit `val` or `def` in Scala 2 can be expressed in Dotty using a regular abstract definition and an implicit alias. E.g., Scala 2's
An abstract implicit `val` or `def` in Scala 2 can be expressed in Dotty using a regular abstract definition and an alias implicit. E.g., Scala 2's
```scala
implicit def symDeco: SymDeco
```
Expand Down
13 changes: 6 additions & 7 deletions docs/docs/reference/contextual-repr/derivation.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ enum Tree[T] derives Eql, Ordering, Pickling {
case Leaf(elem: T)
}
```
The `derives` clause generates repr representatives of the `Eql`, `Ordering`, and `Pickling` traits in the companion object `Tree`:
The `derives` clause generates representatives of the `Eql`, `Ordering`, and `Pickling` traits in the companion object `Tree`:
```scala
repr [T: Eql] of Eql[Tree[T]] = Eql.derived
repr [T: Ordering] of Ordering[Tree[T]] = Ordering.derived
Expand All @@ -19,7 +19,7 @@ repr [T: Pickling] of 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 @@ -93,8 +93,7 @@ is represented as `T *: Unit` since there is no direct syntax for such tuples: `

### The Generic Typeclass

For every class `C[T_1,...,T_n]` with a `derives` clause, the compiler generates in the companion object of `C` an representative of `Generic[C[T_1,...,T_n]]` that follows
the outline below:
For every class `C[T_1,...,T_n]` with a `derives` clause, the compiler generates in the companion object of `C` a representative of `Generic[C[T_1,...,T_n]]` that follows the outline below:
```scala
repr [T_1, ..., T_n] of Generic[C[T_1,...,T_n]] {
type Shape = ...
Expand Down Expand Up @@ -215,7 +214,7 @@ trait Eql[T] {
}
```
We need to implement a method `Eql.derived` that produces a representative of `Eql[T]` provided
there exists evidence of type `Generic[T]`. Here's a possible solution:
there exists a representative 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 All @@ -234,7 +233,7 @@ there exists evidence of type `Generic[T]`. Here's a possible solution:
The implementation of the inline method `derived` creates a representative of `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 Expand Up @@ -302,7 +301,7 @@ The last, and in a sense most interesting part of the derivation is the comparis
case ev: Eql[T] =>
ev.eql(x, y) // (15)
case _ =>
error("No `Eql` representative was found for $T")
error("No `Eql` instance was found for $T")
}
```
`tryEql` is an inline method that takes an element type `T` and two element values of that type as arguments. It is defined using an `implicit match` that tries to find a representative of `Eql[T]`. If a representative `ev` is found, it proceeds by comparing the arguments using `ev.eql`. On the other hand, if no representative is found
Expand Down
8 changes: 4 additions & 4 deletions docs/docs/reference/contextual-repr/extension-methods.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ Then
```scala
List("here", "is", "a", "list").longestStrings
```
is legal everywhere `ops1` is available as a representative. Alternatively, we can define `longestStrings` as a member of a normal object. But then the method has to be brought into scope to be usable as an extension method.
is legal everywhere `ops1` is eligible. Alternatively, we can define `longestStrings` as a member of a normal object. But then the method has to be brought into scope to be usable as an extension method.

```scala
object ops2 extends StringSeqOps
Expand All @@ -80,7 +80,7 @@ So `circle.circumference` translates to `CircleOps.circumference(circle)`, provi

### Representatives for Extension Methods

Representatives that define extension methods can also be defined without a `for` clause. E.g.,
Representatives that define extension methods can also be defined without an `of` clause. E.g.,

```scala
repr StringOps {
Expand All @@ -94,12 +94,12 @@ repr {
def (xs: List[T]) second[T] = xs.tail.head
}
```
If such representatives are anonymous (as in the second clause), their name is synthesized from the name
If such a representative is anonymous (as in the second clause), its name is synthesized from the name
of the first defined extension method.

### Operators

The extension method syntax also applies to the definitions of operators.
The extension method syntax also applies to the definition of operators.
In each case the definition syntax mirrors the way the operator is applied.
Examples:
```scala
Expand Down
6 changes: 3 additions & 3 deletions docs/docs/reference/contextual-repr/import-implied.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ object B {
In the code above, the `import A._` clause of object `B` will import all members
of `A` _except_ the representative `tc`. Conversely, the second import `import repr A._` will import _only_ that representative.

Generally, a normal import clause brings all definitions except representatives into scope whereas an `import repr` clause brings only representatives into scope.
Generally, a normal import clause brings all members except representatives into scope whereas an `import repr` clause brings only representatives into scope.

There are two main benefits arising from these rules:

Expand All @@ -29,7 +29,7 @@ There are two main benefits arising from these rules:
can be anonymous, so the usual recourse of using named imports is not
practical.

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

The rules of representatives above have the consequence that a library
would have to migrate in lockstep with all its users from old style implicits and
Expand All @@ -49,4 +49,4 @@ The following modifications avoid this hurdle to migration.

These rules mean that library users can use `import repr` to access old-style implicits in Scala 3.0,
and will be gently nudged and then forced to do so in later versions. Libraries can then switch to
representation clauses once their user base has migrated.
`repr` clauses once their user base has migrated.
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ The precise steps for synthesizing an argument for a by-name parameter of type `
```
where `lv` is an arbitrary fresh name.

1. This representative 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 argument to an implicit by-name parameter.
1. This representative is not immediately eligible as a candidate for argument inference (making it immediately eligible could result in a loop in the synthesized computation). But it becomes eligible 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 representative `lv`, replace `E` by

Expand Down
2 changes: 1 addition & 1 deletion docs/docs/reference/contextual-repr/inferable-params.md
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` representative 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
6 changes: 4 additions & 2 deletions docs/docs/reference/contextual-repr/instance-defs.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@ An alias can be used to define a representative that is equal to some expression
```scala
repr ctx of ExecutionContext = new ForkJoinPool()
```
This creates a representative `ctx` of type `ExecutionContext` that resolves to the right hand side `new ForkJoinPool()`. The first time a representative of `ExecutionContext` is demanded, a new `ForkJoinPool` is created, which is then returned for this and all subsequent accesses to `ctx`.
This creates a repreentative `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 representatives can be anonymous, e.g.
```scala
Expand All @@ -62,7 +64,7 @@ An alias representative can have type and context parameters just like any other

## Representative Creation

A representative 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 representatives for the same `repr` clause. If a `repr` clause has type parameters or a given clause, a fresh representative is created for each reference.
A representative 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 representatives for the same `repr` clause. If a `repr` clause has type parameters or a given clause, a fresh representative is created for each reference.

## Syntax

Expand Down
2 changes: 1 addition & 1 deletion docs/docs/reference/contextual-repr/motivation.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ The following pages introduce a redesign of contextual abstractions in Scala. Th

2. [Given Clauses](./inferable-params.html) are a new syntax for implicit _parameters_ and their _arguments_. Both are introduced with the same keyword, `given`. This unambiguously aligns parameters and arguments, solving a number of language warts. It also allows us to have several implicit parameter sections, and to have implicit parameters followed by normal ones.

3. [Imports of Representatives](./import-implied.html) are new form of import that specifically imports representatives and nothing else. Representatives _must be_ imported with `import repr`, a plain import will no longer bring them into scope.
3. [Import Repr](./import-implied.html) is new form of import that specifically imports representatives and nothing else. Representatives _must be_ imported with `import repr`, a plain import will no longer bring them into scope.

4. [Implicit Conversions](./conversions.html) are now expressed as representatives of a standard `Conversion` class. All other forms of implicit conversions will be phased out.

Expand Down
6 changes: 3 additions & 3 deletions docs/docs/reference/contextual-repr/multiversal-equality.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ that derives `Eql`, e.g.
```scala
class T derives Eql
```
Alternatively, one can also provide an `Eql` representative directly, like this:
Alternatively, one can also define an `Eql` representative directly, like this:
```scala
repr for Eql[T, T] = Eql.derived
```
Expand Down Expand Up @@ -75,7 +75,7 @@ defined as follows:
def eqlAny[L, R]: Eql[L, R] = Eql.derived
```

Even though `eqlAny` is not declared a representative, the compiler will still
Even though `eqlAny` is not declared as a representative, the compiler will still
construct an `eqlAny` instance as answer to an implicit search for the
type `Eql[L, R]`, unless `L` or `R` have `Eql` representatives
defined on them, or the language feature `strictEquality` is enabled
Expand Down Expand Up @@ -141,7 +141,7 @@ The `Eql` object defines representatives for
- `java.lang.Number`, `java.lang.Boolean`, and `java.lang.Character`,
- `scala.collection.Seq`, and `scala.collection.Set`.

Representative are defined so that every one of these types is has a reflexive `Eql` representative, and the following holds:
Representative are defined so that every one of these types has a reflexive `Eql` representative, and the following holds:

- Primitive numeric types can be compared with each other.
- Primitive numeric types can be compared with subtypes of `java.lang.Number` (and _vice versa_).
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/reference/contextual-repr/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 representatives 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
12 changes: 6 additions & 6 deletions docs/docs/reference/contextual-repr/relationship-implicits.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,17 @@ Representative clauses can be mapped to combinations of implicit objects, classe
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 representatives map to implicit methods. If the representatives 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 representatives map to implicit methods. If the representative 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
repr ec of ExecutionContext = new ForkJoinContext()
repr global of ExecutionContext = new ForkJoinContext()
repr config of 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-repr/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, representatives, extension methods and context bounds
Representatives, extension methods and context bounds
allow a concise and natural expression of _typeclasses_. Typeclasses are just traits
with canonical implementations defined by representatives. Here are some examples of standard typeclasses:

Expand Down