Skip to content

More fixes to contextual-implicit #6378

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 25, 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
32 changes: 16 additions & 16 deletions docs/docs/reference/contextual-implicit/import-implied.md
Original file line number Diff line number Diff line change
@@ -1,51 +1,51 @@
---
layout: doc-page
title: "Evidence Imports"
title: "Import Implicit"
---

A special form of import is used to import evidence values. Example:
A special form of import is used to import implicit values. Example:
```scala
object A {
class TC
evidence tc for TC
implicit tc for TC
def f given TC = ???
}
object B {
import A._
import evidence A._
import implicit A._
}
```
In the code above, the `import A._` clause of object `B` will import all members
of `A` _except_ the evidence `tc`. Conversely, the second import `import evidence A._` will import _only_ that evidence.
of `A` _except_ the implicit `tc`. Conversely, the second import `import implicit A._` will import _only_ that implicit.

Generally, a normal import clause brings all members except evidence values into scope whereas an `import evidence` clause brings only evidence values into scope.
Generally, a normal import clause brings all members except implicit values into scope whereas an `import implicit` clause brings only implicit values into scope.

There are two main benefits arising from these rules:

- It is made clearer where evidence values in scope are coming from. In particular, it is not possible to hide imported evidence values in a long list of regular imports.
- It enables importing all evidence values
without importing anything else. This is particularly important since evidence
- It is made clearer where implicit values in scope are coming from. In particular, it is not possible to hide imported implicit values in a long list of regular imports.
- It enables importing all implicit values
without importing anything else. This is particularly important since implicit
values can be anonymous, so the usual recourse of using named imports is not
practical.

### Relationship with Old-Style Implicits

The rules of evidence imports above have the consequence that a library
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 evidence definitions and evidence imports.
normal imports to new style implicit definitions and `import implicit`.

The following modifications avoid this hurdle to migration.

1. An evidence import 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 or
by an evidence import.
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 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
will give a compiler error.

These rules mean that library users can use `import evidence` to access old-style implicits in Scala 3.0,
These rules mean that library users can use `import implicit` 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
evidence definitions once their user base has migrated.
new-style implicit definitions once their user base has migrated.
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ trait Codec[T] {
def write(x: T): Unit
}

evidence intCodec for Codec[Int] = ???
implicit intCodec for Codec[Int] = ???

evidence optionCodec[T] given (ev: => Codec[T]) for Codec[Option[T]] {
implicit optionCodec[T] given (ev: => Codec[T]) for Codec[Option[T]] {
def write(xo: Option[T]) = xo match {
case Some(x) => ev.write(x)
case None =>
Expand All @@ -33,20 +33,20 @@ 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.

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

```scala
evidence lv for T = ???
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. 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 the implicit `lv`, replace `E` by


```scala
{ evidence lv for T = E; lv }
{ implicit lv for T = E; lv }
```

Otherwise, return `E` unchanged.
Expand Down
22 changes: 11 additions & 11 deletions docs/docs/reference/contextual-implicit/inferable-params.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ call trees where the same value is passed over and over again in long call chain
functions. Given clauses can help here since they enable the compiler to synthesize
repetitive arguments instead of the programmer having to write them explicitly.

For example, given the [evidence definitions](./instance-defs.md) of the previous section,
For example, given the [implicit instances](./instance-defs.md) of the previous section,
a maximum function that works for any arguments for which an ordering exists can be defined as follows:
```scala
def max[T](x: T, y: T) given (ord: Ord[T]): T =
if (ord.compare(x, y) < 1) y else x
```
Here, the part following `given` introduces a constraint that `T` is ordered, or, otherwise put, that evidence for `Ord[T]` exists. The evidence is passed as an _implicit parameter_ to the method. Inside the method, the evidence value can be accessed under the name `ord`.
Here, the part following `given` introduces a constraint that `T` is ordered, or, otherwise put, that evidence for `Ord[T]` exists. The evidence is passed as an _implicit parameter_ to the method. Inside the method, the implicit value can be accessed under the name `ord`.

The `max` method can be applied as follows:
```scala
Expand All @@ -29,14 +29,14 @@ max(List(1, 2, 3), Nil)

## Anonymous Inferable Parameters

In many situations, the name of an evidence parameter of a method need not be mentioned explicitly at all, since it is only used as synthesized evidence 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 synthesized implicit for other constraints. 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)
```
`maximum` takes an evidence parameter of type `Ord` only to pass it on as an implicit argument to `max`. The name of the parameter is left out.
`maximum` takes an implicit parameter of type `Ord` only to pass it on as an implicit argument to `max`. The name of the parameter is left out.

Generally, evidence parameters may be given either as a parameter list `(p_1: T_1, ..., p_n: T_n)` or as a sequence of types, separated by commas.
Generally, implicit parameters may be given either as a parameter list `(p_1: T_1, ..., p_n: T_n)` or as a sequence of types, separated by commas.

## Inferring Complex Arguments

Expand Down Expand Up @@ -66,8 +66,8 @@ There can be several inferable parameter lists in a definition. Example:
```scala
def f given (u: Universe) (x: u.T) given Context = ...

evidence global for Universe { type T = String ... }
evidence ctx for Context { ... }
implicit global for Universe { type T = String ... }
implicit ctx for Context { ... }
```
Then the following calls are all valid (and normalize to the last one)
```scala
Expand All @@ -77,17 +77,17 @@ f("abc") given ctx
(f given global)("abc") given ctx
```

## Summoning the Evidence
## Summoning an Implicit

A method `the` in `Predef` summons the evidence for a given type. For example, the evidence for `Ord[List[Int]]` is generated by
A method `the` in `Predef` summons an implicit for a given type. For example, the implicit for `Ord[List[Int]]` is generated by
```scala
the[Ord[List[Int]]] // reduces to ListOrd given IntOrd
```
The `the` method is simply defined as the (non-widening) identity function over an evidence parameter.
The `the` method is simply defined as the (non-widening) identity function over an implicit parameter.
```scala
def the[T] given (x: T): x.type = x
```
Functions like `the` that have only evidence parameters are also called _context queries_.
Functions like `the` that have only implicit parameters are also called _context queries_.

## Syntax

Expand Down
12 changes: 6 additions & 6 deletions docs/docs/reference/contextual-implicit/typeclasses.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ layout: doc-page
title: "Implementing Typeclasses"
---

Evidence definitions, extension methods and context bounds
Implicit instances, extension methods and context bounds
allow a concise and natural expression of _typeclasses_. Typeclasses are just traits
with canonical implementations defined by evidence definitions. Here are some examples of standard typeclasses:
with canonical implementations defined by implicit instances. Here are some examples of standard typeclasses:

### Semigroups and monoids:

Expand All @@ -20,12 +20,12 @@ object Monoid {
def apply[T] given Monoid[T] = the[Monoid[T]]
}

evidence for Monoid[String] {
implicit for Monoid[String] {
def (x: String) combine (y: String): String = x.concat(y)
def unit: String = ""
}

evidence for Monoid[Int] {
implicit for Monoid[Int] {
def (x: Int) combine (y: Int): Int = x + y
def unit: Int = 0
}
Expand All @@ -48,14 +48,14 @@ trait Monad[F[_]] extends Functor[F] {
def pure[A](x: A): F[A]
}

evidence ListMonad for Monad[List] {
implicit ListMonad for Monad[List] {
def (xs: List[A]) flatMap [A, B] (f: A => List[B]): List[B] =
xs.flatMap(f)
def pure[A](x: A): List[A] =
List(x)
}

evidence ReaderMonad[Ctx] for Monad[[X] => Ctx => X] {
implicit ReaderMonad[Ctx] for Monad[[X] => Ctx => X] {
def (r: Ctx => A) flatMap [A, B] (f: A => Ctx => B): Ctx => B =
ctx => f(r(ctx))(ctx)
def pure[A](x: A): Ctx => A =
Expand Down