Skip to content

Commit 5ff0d9b

Browse files
committed
Trial: given ... with syntax
For given instances: given ... For context parameters ... with ... For context functions A ?=> B Change doc pages accordingly
1 parent 9ab1842 commit 5ff0d9b

26 files changed

+637
-610
lines changed

docs/docs/internals/syntax.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ yield
103103
### Soft keywords
104104

105105
```
106-
derives extension inline opaque open
106+
as derives extension inline on opaque open
107107
~ * | & + -
108108
```
109109

docs/docs/reference/contextual/implicit-by-name-parameters.md renamed to docs/docs/reference/contextual/by-name-context-parameters.md

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
---
22
layout: doc-page
3-
title: "Implicit By-Name Parameters"
3+
title: "By-Name Context Parameters"
44
---
55

6-
Implicit parameters can be declared by-name to avoid a divergent inferred expansion. Example:
6+
Context parameters can be declared by-name to avoid a divergent inferred expansion. Example:
77

88
```scala
99
trait Codec[T] {
1010
def write(x: T): Unit
1111
}
1212

13-
given intCodec: Codec[Int] = ???
13+
given intCodec as Codec[Int] = ???
1414

15-
given optionCodec[T]: (ev: => Codec[T]) => Codec[Option[T]] {
15+
given optionCodec[T] with (ev: => Codec[T]) as Codec[Option[T]] {
1616
def write(xo: Option[T]) = xo match {
1717
case Some(x) => ev.write(x)
1818
case None =>
@@ -24,29 +24,29 @@ val s = summon[Codec[Option[Int]]]
2424
s.write(Some(33))
2525
s.write(None)
2626
```
27-
As is the case for a normal by-name parameter, the argument for the implicit parameter `ev`
27+
As is the case for a normal by-name parameter, the argument for the context parameter `ev`
2828
is evaluated on demand. In the example above, if the option value `x` is `None`, it is
2929
not evaluated at all.
3030

31-
The synthesized argument for an implicit parameter is backed by a local val
31+
The synthesized argument for a context parameter is backed by a local val
3232
if this is necessary to prevent an otherwise diverging expansion.
3333

34-
The precise steps for synthesizing an argument for an implicit by-name parameter of type `=> T` are as follows.
34+
The precise steps for synthesizing an argument for a by-name context parameter of type `=> T` are as follows.
3535

36-
1. Create a new given instance of type `T`:
36+
1. Create a new given of type `T`:
3737

3838
```scala
39-
given lv: T = ???
39+
given lv as T = ???
4040
```
4141
where `lv` is an arbitrary fresh name.
4242

43-
1. This given 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 argument to an implicit by-name parameter.
43+
1. This given 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 a by-name context parameter.
4444

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

4747

4848
```scala
49-
{ given lv: T = E; lv }
49+
{ given lv as T = E; lv }
5050
```
5151

5252
Otherwise, return `E` unchanged.
@@ -55,7 +55,7 @@ In the example above, the definition of `s` would be expanded as follows.
5555

5656
```scala
5757
val s = summon[Test.Codec[Option[Int]]](
58-
optionCodec[Int](intCodec)
58+
optionCodec[Int].with(intCodec)
5959
)
6060
```
6161

docs/docs/reference/contextual/context-bounds.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,17 @@ title: "Context Bounds"
55

66
## Context Bounds
77

8-
A context bound is a shorthand for expressing the 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:
8+
A context bound is a shorthand for expressing the common pattern of a context parameter that depends on a type parameter. Using a context bound, the `maximum` function of the last section can be written like this:
99
```scala
1010
def maximum[T: Ord](xs: List[T]): T = xs.reduceLeft(max)
1111
```
12-
A bound like `: Ord` on a type parameter `T` of a method or class indicates an implicit parameter `(given Ord[T])`. The implicit parameter(s) generated from context bounds come last in the definition of the containing method or class. E.g.,
12+
A bound like `: Ord` on a type parameter `T` of a method or class indicates a context parameter `with Ord[T]`. The context parameter(s) generated from context bounds come last in the definition of the containing method or class. E.g.,
1313
```scala
14-
def f[T: C1 : C2, U: C3](x: T)(given y: U, z: V): R
14+
def f[T: C1 : C2, U: C3](x: T) with (y: U, z: V) : R
1515
```
1616
would expand to
1717
```scala
18-
def f[T, U](x: T)(given y: U, z: V)(given C1[T], C2[T], C3[U]): R
18+
def f[T, U](x: T) with (y: U, z: V) with C1[T], C2[T], C3[U]) : R
1919
```
2020
Context bounds can be combined with subtype bounds. If both are present, subtype bounds come first, e.g.
2121
```scala
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
---
2+
layout: doc-page
3+
title: "Context Functions - More Details"
4+
---
5+
6+
## Syntax
7+
8+
Type ::= ...
9+
| FunArgTypes ‘?=>’ Type
10+
Expr ::= ...
11+
| FunParams ‘?=>’ Expr
12+
13+
Context function types associate to the right, e.g.
14+
`S ?=> T ?=> U` is the same as `S ?=> (T ?=> U)`.
15+
16+
## Implementation
17+
18+
Context function types are shorthands for class types that define `apply`
19+
methods with context parameters. Specifically, the `N`-ary function type
20+
`T1, ..., TN => R` is a shorthand for the class type
21+
`ContextFunctionN[T1 , ... , TN, R]`. Such class types are assumed to have the following definitions, for any value of `N >= 1`:
22+
```scala
23+
package scala
24+
trait ContextFunctionN[-T1 , ... , -TN, +R] {
25+
def apply with (x1: T1 , ... , xN: TN) : R
26+
}
27+
```
28+
Context function types erase to normal function types, so these classes are
29+
generated on the fly for typechecking, but not realized in actual code.
30+
31+
Context function literals `(x1: T1, ..., xn: Tn) ?=> e` map
32+
context parameters `xi` of types `Ti` to the result of evaluating the expression `e`.
33+
The scope of each context parameter `xi` is `e`. The parameters must have pairwise distinct names.
34+
35+
If the expected type of the context function literal is of the form
36+
`scala.ContextFunctionN[S1, ..., Sn, R]`, the expected type of `e` is `R` and
37+
the type `Ti` of any of the parameters `xi` can be omitted, in which case `Ti
38+
= Si` is assumed. If the expected type of the context function literal is
39+
some other type, all context parameter types must be explicitly given, and the expected type of `e` is undefined.
40+
The type of the context function literal is `scala.ContextFunctionN[S1, ...,Sn, T]`, where `T` is the widened
41+
type of `e`. `T` must be equivalent to a type which does not refer to any of
42+
the context parameters `xi`.
43+
44+
The context function literal is evaluated as the instance creation
45+
expression
46+
```scala
47+
new scala.ContextFunctionN[T1, ..., Tn, T] {
48+
def apply with (x1: T1, ..., xn: Tn) : T = e
49+
}
50+
```
51+
A context parameter may also be a wildcard represented by an underscore `_`. In that case, a fresh name for the parameter is chosen arbitrarily.
52+
53+
Note: The closing paragraph of the
54+
[Anonymous Functions section](https://www.scala-lang.org/files/archive/spec/2.12/06-expressions.html#anonymous-functions)
55+
of Scala 2.12 is subsumed by context function types and should be removed.
56+
57+
Context function literals `(x1: T1, ..., xn: Tn) ?=> e` are
58+
automatically created for any expression `e` whose expected type is
59+
`scala.ContextFunctionN[T1, ..., Tn, R]`, unless `e` is
60+
itself a context function literal. This is analogous to the automatic
61+
insertion of `scala.Function0` around expressions in by-name argument position.
62+
63+
Context function types generalize to `N > 22` in the same way that function types do, see [the corresponding
64+
documentation](../dropped-features/limit22.md).
65+
66+
## Examples
67+
68+
See the section on Expressiveness from [Simplicitly: foundations and
69+
applications of implicit function
70+
types](https://dl.acm.org/citation.cfm?id=3158130).
71+
72+
### Type Checking
73+
74+
After desugaring no additional typing rules are required for context function types.
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
---
2+
layout: doc-page
3+
title: "Context Functions"
4+
---
5+
6+
_Context functions_ are functions with (only) context parameters.
7+
Their types are _context function types_. Here is an example of a context function type:
8+
9+
```scala
10+
type Executable[T] = ExecutionContext ?=> T
11+
```
12+
Context function are written using `?=>` as the "arrow" sign.
13+
They are applied to synthesized arguments, in
14+
the same way methods with context parameters is applied. For instance:
15+
```scala
16+
given ec as ExecutionContext = ...
17+
18+
def f(x: Int): Executable[Int] = ...
19+
20+
f(2).with(ec) // explicit argument
21+
f(2) // argument is inferred
22+
```
23+
Conversely, if the expected type of an expression `E` is a context function type
24+
`(T_1, ..., T_n) ?=> U` and `E` is not already an
25+
context function literal, `E` is converted to an context function literal by rewriting to
26+
```scala
27+
(x_1: T1, ..., x_n: Tn) ?=> E
28+
```
29+
where the names `x_1`, ..., `x_n` are arbitrary. This expansion is performed
30+
before the expression `E` is typechecked, which means that `x_1`, ..., `x_n`
31+
are available as givens in `E`.
32+
33+
Like their types, context function literals are written using `?=>` as the arrow between parameters and results. They differ from normal function literals in that their types are context function types.
34+
35+
For example, continuing with the previous definitions,
36+
```scala
37+
def g(arg: Executable[Int]) = ...
38+
39+
g(22) // is expanded to g((ev: ExecutionContext) ?=> 22)
40+
41+
g(f(2)) // is expanded to g((ev: ExecutionContext) ?=> f(2).with(ev))
42+
43+
g((ctx: ExecutionContext) ?=> f(22).with(ctx)) // is left as it is
44+
```
45+
### Example: Builder Pattern
46+
47+
Context function types have considerable expressive power. For
48+
instance, here is how they can support the "builder pattern", where
49+
the aim is to construct tables like this:
50+
```scala
51+
table {
52+
row {
53+
cell("top left")
54+
cell("top right")
55+
}
56+
row {
57+
cell("bottom left")
58+
cell("bottom right")
59+
}
60+
}
61+
```
62+
The idea is to define classes for `Table` and `Row` that allow
63+
addition of elements via `add`:
64+
```scala
65+
class Table {
66+
val rows = new ArrayBuffer[Row]
67+
def add(r: Row): Unit = rows += r
68+
override def toString = rows.mkString("Table(", ", ", ")")
69+
}
70+
71+
class Row {
72+
val cells = new ArrayBuffer[Cell]
73+
def add(c: Cell): Unit = cells += c
74+
override def toString = cells.mkString("Row(", ", ", ")")
75+
}
76+
77+
case class Cell(elem: String)
78+
```
79+
Then, the `table`, `row` and `cell` constructor methods can be defined
80+
with context function types as parameters to avoid the plumbing boilerplate
81+
that would otherwise be necessary.
82+
```scala
83+
def table(init: Table ?=> Unit) = {
84+
given t as Table
85+
init
86+
t
87+
}
88+
89+
def row(init: Row ?=> Unit) with (t: Table) = {
90+
given r as Row
91+
init
92+
t.add(r)
93+
}
94+
95+
def cell(str: String) with (r: Row) =
96+
r.add(new Cell(str))
97+
```
98+
With that setup, the table construction code above compiles and expands to:
99+
```scala
100+
table { ($t: Table) ?=>
101+
102+
row { ($r: Row) ?=>
103+
cell("top left").with($r)
104+
cell("top right").with($r)
105+
}.with($t)
106+
107+
row { ($r: Row) ?=>
108+
cell("bottom left").with($r)
109+
cell("bottom right").with($r)
110+
}.with($t)
111+
}
112+
```
113+
### Example: Postconditions
114+
115+
As a larger example, here is a way to define constructs for checking arbitrary postconditions using an extension method `ensuring` so that the checked result can be referred to simply by `result`. The example combines opaque aliases, context function types, and extension methods to provide a zero-overhead abstraction.
116+
117+
```scala
118+
object PostConditions {
119+
opaque type WrappedResult[T] = T
120+
121+
def result[T] with (r: WrappedResult[T]) : T = r
122+
123+
def (x: T) ensuring[T](condition: WrappedResult[T] ?=> Boolean): T = {
124+
assert(condition.with(x))
125+
x
126+
}
127+
}
128+
import PostConditions.{ensuring, result}
129+
130+
val s = List(1, 2, 3).sum.ensuring(result == 6)
131+
```
132+
**Explanations**: We use a context function type `WrappedResult[T] ?=> Boolean`
133+
as the type of the condition of `ensuring`. An argument to `ensuring` such as
134+
`(result == 6)` will therefore have a given of type `WrappedResult[T]` in
135+
scope to pass along to the `result` method. `WrappedResult` is a fresh type, to make sure
136+
that we do not get unwanted givens in scope (this is good practice in all cases
137+
where context parameters are involved). Since `WrappedResult` is an opaque type alias, its
138+
values need not be boxed, and since `ensuring` is added as an extension method, its argument
139+
does not need boxing either. Hence, the implementation of `ensuring` is as about as efficient
140+
as the best possible code one could write by hand:
141+
142+
```scala
143+
{ val result = List(1, 2, 3).sum
144+
assert(result == 6)
145+
result
146+
}
147+
```
148+
### Reference
149+
150+
For more info, see the [blog article](https://www.scala-lang.org/blog/2016/12/07/implicit-function-types.html),
151+
(which uses a different syntax that has been superseded).
152+
153+
[More details](./implicit-function-types-spec.md)

0 commit comments

Comments
 (0)