Skip to content

Commit bc2bcc0

Browse files
authored
Merge pull request #6411 from dotty-staging/try-contextual-delegate
Trial: Rename implied-for to delegate-for
2 parents 29aeb02 + cc15753 commit bc2bcc0

14 files changed

+1714
-0
lines changed
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
---
2+
layout: doc-page
3+
title: "Context Bounds"
4+
---
5+
6+
## Context Bounds
7+
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:
9+
```scala
10+
def maximum[T: Ord](xs: List[T]): T = xs.reduceLeft(max)
11+
```
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.,
13+
```scala
14+
def f[T: C1 : C2, U: C3](x: T) given (y: U, z: V): R
15+
```
16+
would expand to
17+
```scala
18+
def f[T, U](x: T) given (y: U, z: V) given C1[T], C2[T], C3[U]: R
19+
```
20+
Context bounds can be combined with subtype bounds. If both are present, subtype bounds come first, e.g.
21+
```scala
22+
def g[T <: B : C](x: T): R = ...
23+
```
24+
25+
## Syntax
26+
27+
```
28+
TypeParamBounds ::= [SubtypeBounds] {ContextBound}
29+
ContextBound ::= ‘:’ Type
30+
```
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
---
2+
layout: doc-page
3+
title: "Implicit Conversions"
4+
---
5+
6+
Implicit conversions are defined by delegates for the `scala.Conversion` class.
7+
This class is defined in package `scala` as follows:
8+
```scala
9+
abstract class Conversion[-T, +U] extends (T => U)
10+
```
11+
For example, here is an implicit conversion from `String` to `Token`:
12+
```scala
13+
delegate for Conversion[String, Token] {
14+
def apply(str: String): Token = new KeyWord(str)
15+
}
16+
```
17+
Using an alias delegate this can be expressed more concisely as:
18+
```scala
19+
delegate for Conversion[String, Token] = new KeyWord(_)
20+
```
21+
An implicit conversion is applied automatically by the compiler in three situations:
22+
23+
1. If an expression `e` has type `T`, and `T` does not conform to the expression's expected type `S`.
24+
2. In a selection `e.m` with `e` of type `T`, but `T` defines no member `m`.
25+
3. In an application `e.m(args)` with `e` of type `T`, if `T` does define
26+
some member(s) named `m`, but none of these members can be applied to the arguments `args`.
27+
28+
In the first case, the compiler looks for a delegate for
29+
`scala.Conversion` that maps an argument of type `T` to type `S`. In the second and third
30+
case, it looks for a delegate for `scala.Conversion` that maps an argument of type `T`
31+
to a type that defines a member `m` which can be applied to `args` if present.
32+
If such a delegate `C` is found, the expression `e` is replaced by `C.apply(e)`.
33+
34+
## Examples
35+
36+
1. The `Predef` package contains "auto-boxing" conversions that map
37+
primitive number types to subclasses of `java.lang.Number`. For instance, the
38+
conversion from `Int` to `java.lang.Integer` can be defined as follows:
39+
```scala
40+
delegate int2Integer for Conversion[Int, java.lang.Integer] =
41+
java.lang.Integer.valueOf(_)
42+
```
43+
44+
2. The "magnet" pattern is sometimes used to express many variants of a method. Instead of defining overloaded versions of the method, one can also let the method take one or more arguments of specially defined "magnet" types, into which various argument types can be converted. E.g.
45+
```scala
46+
object Completions {
47+
48+
// The argument "magnet" type
49+
enum CompletionArg {
50+
case Error(s: String)
51+
case Response(f: Future[HttpResponse])
52+
case Status(code: Future[StatusCode])
53+
}
54+
object CompletionArg {
55+
56+
// conversions defining the possible arguments to pass to `complete`
57+
// these always come with CompletionArg
58+
// They can be invoked explicitly, e.g.
59+
//
60+
// CompletionArg.fromStatusCode(statusCode)
61+
62+
delegate fromString for Conversion[String, CompletionArg] = Error(_)
63+
delegate fromFuture for Conversion[Future[HttpResponse], CompletionArg] = Response(_)
64+
delegate fromStatusCode for Conversion[Future[StatusCode], CompletionArg] = Status(_)
65+
}
66+
import CompletionArg._
67+
68+
def complete[T](arg: CompletionArg) = arg match {
69+
case Error(s) => ...
70+
case Response(f) => ...
71+
case Status(code) => ...
72+
}
73+
}
74+
```
75+
This setup is more complicated than simple overloading of `complete`, but it can still be useful if normal overloading is not available (as in the case above, since we cannot have two overloaded methods that take `Future[...]` arguments), or if normal overloading would lead to a combinatorial explosion of variants.

0 commit comments

Comments
 (0)