Skip to content

Add missing files to contextual-instance #6387

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
Apr 26, 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
39 changes: 20 additions & 19 deletions docs/docs/reference/contextual-instance/derivation.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ enum Tree[T] derives Eql, Ordering, Pickling {
case Leaf(elem: T)
}
```
The `derives` clause generates evidence for the `Eql`, `Ordering`, and `Pickling` traits in the companion object `Tree`:
The `derives` clause generates implicit instances of the `Eql`, `Ordering`, and `Pickling` traits in the companion object `Tree`:
```scala
evidence [T: Eql] for Eql[Tree[T]] = Eql.derived
evidence [T: Ordering] for Ordering[Tree[T]] = Ordering.derived
evidence [T: Pickling] for Pickling[Tree[T]] = Pickling.derived
instance [T: Eql] of Eql[Tree[T]] = Eql.derived
instance [T: Ordering] of Ordering[Tree[T]] = Ordering.derived
instance [T: Pickling] of Pickling[Tree[T]] = Pickling.derived
```

### Deriving Types
Expand Down Expand Up @@ -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. Evidence for `Generic` is generated automatically for any type that derives a typeclass with a `derived`
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. An implicit instance of `Generic` is 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 @@ -94,9 +94,9 @@ 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` evidence for `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` an implicit instance of `Generic[C[T_1,...,T_n]]` that follows the outline below:
```scala
evidence [T_1, ..., T_n] for Generic[C[T_1,...,T_n]] {
instance [T_1, ..., T_n] of Generic[C[T_1,...,T_n]] {
type Shape = ...
...
}
Expand All @@ -114,7 +114,7 @@ would produce:
object Result {
import scala.compiletime.Shape._

evidence [T, E] for Generic[Result[T, E]] {
instance [T, E] of Generic[Result[T, E]] {
type Shape = Cases[(
Case[Ok[T], T *: Unit],
Case[Err[E], E *: Unit]
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 evidence of type `Generic[T]`. Here's a possible solution:
We need to implement a method `Eql.derived` that produces an implicit instance of `Eql[T]` provided
there exists an implicit instance of `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 +234,7 @@ there exists evidence of type `Generic[T]`. Here's a possible solution:
The implementation of the inline method `derived` creates an instance 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` instance `(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 @@ -298,14 +298,14 @@ non-empty, say of form `elem *: elems1`, the following code is produced:

The last, and in a sense most interesting part of the derivation is the comparison of a pair of element values in `tryEql`. Here is the definition of this method:
```scala
inline def tryEql[T](x: T, y: T) = implicit match {
inline def tryEql[T](x: T, y: T) = instance match {
case ev: Eql[T] =>
ev.eql(x, y) // (15)
case _ =>
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 `evidence match` that tries to find evidence for `Eql[T]`. If an instance `ev` is found, it proceeds by comparing the arguments using `ev.eql`. On the other hand, if no instance is found
`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 `instance match` that tries to find an implicit instance of `Eql[T]`. If an instance `ev` is found, it proceeds by comparing the arguments using `ev.eql`. On the other hand, if no instance is found
this signals a compilation error: the user tried a generic derivation of `Eql` for a class with an element type that does not support an `Eql` instance itself. The error is signaled by
calling the `error` method defined in `scala.compiletime`.

Expand All @@ -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
evidence [T] given (elemEq: Eql[T]) for Eql[Tree[T]] {
instance [T] given (elemEq: Eql[T]) of Eql[Tree[T]] {
def eql(x: Tree[T], y: Tree[T]): Boolean = {
val ev = the[Generic[Tree[T]]]
val mx = ev.reflect(x)
Expand All @@ -333,21 +333,22 @@ evidence [T] given (elemEq: Eql[T]) for Eql[Tree[T]] {
}
```

One important difference between this approach and Scala-2 typeclass derivation frameworks such as Shapeless or Magnolia is that no automatic attempt is made to generate typeclass instances of elements recursively using the generic derivation framework. There must be an evidence value of type `Eql[T]` (which can of course be produced in turn using `Eql.derived`), or the compilation will fail. The advantage of this more restrictive approach to typeclass derivation is that it avoids uncontrolled transitive typeclass derivation by design. This keeps code sizes smaller, compile times lower, and is generally more predictable.
One important difference between this approach and Scala-2 typeclass derivation frameworks such as Shapeless or Magnolia is that no automatic attempt is made to generate typeclass instances of elements recursively using the generic derivation framework. There must be an implicit instance of type `Eql[T]` (which can of course be produced in turn using `Eql.derived`), or the compilation will fail. The advantage of this more restrictive approach to typeclass derivation is that it avoids uncontrolled transitive typeclass derivation by design. This keeps code sizes smaller, compile times lower, and is generally more predictable.

### Derived Instances Elsewhere

Sometimes one would like to derive a typeclass instance for an ADT after the ADT is defined, without being able to change the code of the ADT itself.
To do this, simply define an instance with the `derived` method of the typeclass as right-hand side. E.g, to implement `Ordering` for `Option`, define:
```scala
evidence [T: Ordering]: Ordering[Option[T]] = Ordering.derived
instance [T: Ordering] of Ordering[Option[T]] = Ordering.derived
```
Usually, the `Ordering.derived` clause has an inferable parameter of type
`Generic[Option[T]]`. Since the `Option` trait has a `derives` clause,
the necessary evidence is already present in the companion object of `Option`.
If the ADT in question does not have a `derives` clause, evidence for `Generic`
the necessary implicit instance is already present in the companion object of `Option`.
If the ADT in question does not have a `derives` clause, an implicit instance of `Generic`
would still be synthesized by the compiler at the point where `derived` is called.
This is similar to the situation with type tags or class tags: If no evidence is found, the compiler will synthesize it.
This is similar to the situation with type tags or class tags: If no instance is found,
the compiler will synthesize one.

### Syntax

Expand Down
22 changes: 11 additions & 11 deletions docs/docs/reference/contextual-instance/extension-methods.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ When is an extension method applicable? There are two possibilities.

- An extension method is applicable if it is visible under a simple name, by being defined
or inherited or imported in a scope enclosing the application.
- An extension method is applicable if it is a member of some evidence value at the point of the application.
- An extension method is applicable if it is a member of some implicit instance at the point of the application.

As an example, consider an extension method `longestStrings` on `String` defined in a trait `StringSeqOps`.

Expand All @@ -48,15 +48,15 @@ trait StringSeqOps {
}
}
```
We can make the extension method available by defining evidence for `StringSeqOps`, like this:
We can make the extension method available by defining an implicit instance of `StringSeqOps`, like this:
```scala
instance ops1 of StringSeqOps
```
Then
```scala
List("here", "is", "a", "list").longestStrings
```
is legal everywhere `ops1` is available as evidence. 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 available as an implicit. 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 @@ -69,32 +69,32 @@ Assume a selection `e.m[Ts]` where `m` is not a member of `e`, where the type ar
and where `T` is the expected type. The following two rewritings are tried in order:

1. The selection is rewritten to `m[Ts](e)`.
2. If the first rewriting does not typecheck with expected type `T`, and there is evidence `i`
in either the current scope or in the evidence scope of `T`, and `i` defines an extension
2. If the first rewriting does not typecheck with expected type `T`, and there is an implicit `i`
in either the current scope or in the implicit scope of `T`, and `i` defines an extension
method named `m`, then selection is expanded to `i.m[Ts](e)`.
This second rewriting is attempted at the time where the compiler also tries an implicit conversion
from `T` to a type containing `m`. If there is more than one way of rewriting, an ambiguity error results.

So `circle.circumference` translates to `CircleOps.circumference(circle)`, provided
`circle` has type `Circle` and `CircleOps` is an eligible evidence value (i.e. it is visible at the point of call or it is defined in the companion object of `Circle`).
`circle` has type `Circle` and `CircleOps` is an eligible implicit (i.e. it is visible at the point of call or it is defined in the companion object of `Circle`).

### Evidence for Extension Methods
### Implicit Instances for Extension Methods

Evidence that defines extension methods can also be defined without a `for` clause. E.g.,
Implicits that wrap extension methods can also be defined without an `of` clause. E.g.,

```scala
evidence StringOps {
instance StringOps {
def (xs: Seq[String]) longestStrings: Seq[String] = {
val maxLength = xs.map(_.length).max
xs.filter(_.length == maxLength)
}
}

evidence {
instance {
def (xs: List[T]) second[T] = xs.tail.head
}
```
If such an evidence is anonymous (as in the second example above), its name is synthesized from the name
If such an instance is anonymous (as in the second example above), its name is synthesized from the name
of the first defined extension method.

### Operators
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
layout: doc-page
title: "Implicit By-Name Parameters"
title: "Inferable By-Name Parameters"
---

Inferable by-name parameters can be used to avoid a divergent inferred expansion. Example:
Expand Down Expand Up @@ -42,7 +42,7 @@ The precise steps for constructing an inferable argument for a by-name parameter

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. If this search succeeds with expression `E`, and `E` contains references to the evidence `lv`, replace `E` by
1. If this search succeeds with expression `E`, and `E` contains references to `lv`, replace `E` by


```scala
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ that derives `Eql`, e.g.
```scala
class T derives Eql
```
Alternatively, one can also provide the derived evidence directly, like this:
Alternatively, one can also provide the derived instance directly, like this:
```scala
instance of Eql[T, T] = Eql.derived
```
Expand Down Expand Up @@ -74,7 +74,7 @@ defined as follows:
def eqlAny[L, R]: Eql[L, R] = Eql.derived
```

Even though `eqlAny` is not declared as `evidence`, the compiler will still
Even though `eqlAny` is not declared as `instance`, 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` instances
defined on them, or the language feature `strictEquality` is enabled
Expand Down Expand Up @@ -140,7 +140,7 @@ The `Eql` object defines implicit instances for
- `java.lang.Number`, `java.lang.Boolean`, and `java.lang.Character`,
- `scala.collection.Seq`, and `scala.collection.Set`.

Instances are defined so that everyone of these types is has a reflexive `Eql` evidence, and the following holds:
Instances are defined so that everyone of these types has a reflexive `Eql` instance, 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
6 changes: 3 additions & 3 deletions docs/docs/reference/contextual-instance/query-types.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ context query literal, `E` is converted to a context query literal by rewriting
```
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 evidence in `E`.
are available as implicits in `E`.

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

Expand Down Expand Up @@ -139,9 +139,9 @@ object Test {
```
**Explanations**: We use a context query type `given WrappedResult[T] => Boolean`
as the type of the condition of `ensuring`. An argument to `ensuring` such as
`(result == 6)` will therefore have evidence of type `WrappedResult[T]` in
`(result == 6)` will therefore have an implicit instance of type `WrappedResult[T]` in
scope to pass along to the `result` method. `WrappedResult` is a fresh type, to make sure
that we do not get unwanted evidence types in scope (this is good practice in all cases
that we do not get unwanted implicit instances in scope (this is good practice in all cases
where given clauses are involved). Since `WrappedResult` is an opaque type alias, its
values need not be boxed, and since `ensuring` is added as an extension method, its argument
does not need boxing either. Hence, the implementation of `ensuring` is as about as efficient
Expand Down