Skip to content

Commit 5439c6e

Browse files
committed
Change docs to given/using syntax
1 parent bbd0c5f commit 5439c6e

14 files changed

+119
-152
lines changed

docs/docs/reference/contextual/by-name-context-parameters.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ trait Codec[T] {
1212

1313
given intCodec as Codec[Int] = ???
1414

15-
given optionCodec[T] with (ev: => Codec[T]) as Codec[Option[T]] {
15+
given optionCodec[T](using 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 =>
@@ -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].with(intCodec)
58+
optionCodec[Int](using intCodec)
5959
)
6060
```
6161

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

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@ def maximum[T: Ord](xs: List[T]): T = xs.reduceLeft(max)
1111
```
1212
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) with (y: U, z: V) : R
14+
def f[T: C1 : C2, U: C3](x: T)(using y: U, z: V): R
1515
```
1616
would expand to
1717
```scala
18-
def f[T, U](x: T) with (y: U, z: V) with C1[T], C2[T], C3[U]) : R
18+
def f[T, U](x: T)(using y: U, z: V)(using 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
@@ -25,12 +25,11 @@ def g[T <: B : C](x: T): R = ...
2525
### Migration
2626

2727
To ease migration, context bounds in Dotty map in Scala 3.0 to old-style implicit parameters
28-
for which arguments can be passed either using `.with(...)` or using a normal application.
29-
From Scala 3.1 on, they will map to context parameters instead, as is described above.
28+
for which arguments can be passed either with a `(using ...)` clause or with a normal application. From Scala 3.1 on, they will map to context parameters instead, as is described above.
3029

3130
If the source version is `3.1` and the `-migration` command-line option is set, any pairing of an evidence
3231
context parameter stemming from a context bound with a normal argument will give a migration
33-
warning. The warning indicates that a `.with(...)` clause should be used instead. The rewrite can be
32+
warning. The warning indicates that a `(using ...)` clause is needed instead. The rewrite can be
3433
done automatically under `-rewrite`.
3534

3635
### Syntax

docs/docs/reference/contextual/context-functions-spec.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ methods with context parameters. Specifically, the `N`-ary function type
2222
```scala
2323
package scala
2424
trait ContextFunctionN[-T1 , ... , -TN, +R] {
25-
def apply with (x1: T1 , ... , xN: TN) : R
25+
def apply(using x1: T1 , ... , xN: TN): R
2626
}
2727
```
2828
Context function types erase to normal function types, so these classes are
@@ -45,7 +45,7 @@ The context function literal is evaluated as the instance creation
4545
expression
4646
```scala
4747
new scala.ContextFunctionN[T1, ..., Tn, T] {
48-
def apply with (x1: T1, ..., xn: Tn) : T = e
48+
def apply(using x1: T1, ..., xn: Tn): T = e
4949
}
5050
```
5151
A context parameter may also be a wildcard represented by an underscore `_`. In that case, a fresh name for the parameter is chosen arbitrarily.

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

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ the same way methods with context parameters is applied. For instance:
1717

1818
def f(x: Int): Executable[Int] = ...
1919

20-
f(2).with(ec) // explicit argument
20+
f(2)(using ec) // explicit argument
2121
f(2) // argument is inferred
2222
```
2323
Conversely, if the expected type of an expression `E` is a context function type
@@ -38,9 +38,9 @@ For example, continuing with the previous definitions,
3838

3939
g(22) // is expanded to g((ev: ExecutionContext) ?=> 22)
4040

41-
g(f(2)) // is expanded to g((ev: ExecutionContext) ?=> f(2).with(ev))
41+
g(f(2)) // is expanded to g((ev: ExecutionContext) ?=> f(2)(using ev))
4242

43-
g((ctx: ExecutionContext) ?=> f(22).with(ctx)) // is left as it is
43+
g((ctx: ExecutionContext) ?=> f(22)(using ctx)) // is left as it is
4444
```
4545
### Example: Builder Pattern
4646

@@ -86,28 +86,28 @@ that would otherwise be necessary.
8686
t
8787
}
8888

89-
def row(init: Row ?=> Unit) with (t: Table) = {
89+
def row(init: Row ?=> Unit)(using t: Table) = {
9090
given r as Row
9191
init
9292
t.add(r)
9393
}
9494

95-
def cell(str: String) with (r: Row) =
95+
def cell(str: String)(using r: Row) =
9696
r.add(new Cell(str))
9797
```
9898
With that setup, the table construction code above compiles and expands to:
9999
```scala
100100
table { ($t: Table) ?=>
101101

102102
row { ($r: Row) ?=>
103-
cell("top left").with($r)
104-
cell("top right").with($r)
105-
}.with($t)
103+
cell("top left")(using $r)
104+
cell("top right")(using $r)
105+
}(using $t)
106106

107107
row { ($r: Row) ?=>
108-
cell("bottom left").with($r)
109-
cell("bottom right").with($r)
110-
}.with($t)
108+
cell("bottom left")(using $r)
109+
cell("bottom right")(using $r)
110+
}(using $t)
111111
}
112112
```
113113
### Example: Postconditions
@@ -118,10 +118,10 @@ As a larger example, here is a way to define constructs for checking arbitrary p
118118
object PostConditions {
119119
opaque type WrappedResult[T] = T
120120

121-
def result[T] with (r: WrappedResult[T]) : T = r
121+
def result[T](using r: WrappedResult[T]): T = r
122122

123-
def (x: T) ensuring[T](condition: WrappedResult[T] ?=> Boolean): T = {
124-
assert(condition.with(x))
123+
def (x: T).ensuring[T](condition: WrappedResult[T] ?=> Boolean): T = {
124+
assert(condition(using x))
125125
x
126126
}
127127
}
Lines changed: 27 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
layout: doc-page
3-
title: "Context Parameters"
3+
title: "Using Clauses"
44
---
55

66
Functional programming tends to express most dependencies as simple function parameterization.
@@ -11,22 +11,19 @@ repetitive arguments instead of the programmer having to write them explicitly.
1111
For example, with the [given instances](./givens.md) defined previously,
1212
a maximum function that works for any arguments for which an ordering exists can be defined as follows:
1313
```scala
14-
def max[T](x: T, y: T) with (ord: Ord[T]) : T =
14+
def max[T](x: T, y: T)(using ord: Ord[T]): T =
1515
if ord.compare(x, y) < 0 then y else x
1616
```
17-
Here, `ord` is a _context parameter_ introduced with a `with` clause.
17+
Here, `ord` is a _context parameter_ introduced with a `using` clause.
1818
The `max` method can be applied as follows:
1919
```scala
20-
max(2, 3).with(intOrd)
20+
max(2, 3)(using intOrd)
2121
```
22-
The `.with(intOrd)` part passes `intOrd` as an argument for the `ord` parameter. But the point of
23-
context parameters is that this argument can also be left out (and it usually is). So the following
24-
applications are equally valid:
22+
The `(using intOrd)` part passes `intOrd` as an argument for the `ord` parameter. But the point of context parameters is that this argument can also be left out (and it usually is). So the following applications are equally valid:
2523
```scala
2624
max(2, 3)
2725
max(List(1, 2, 3), Nil)
2826
```
29-
Formatting hint: For legibility it is recommended to always leave one space after a `with` clause before following it with another lexeme.
3027

3128
## Anonymous Context Parameters
3229

@@ -35,45 +32,41 @@ mentioned explicitly at all, since it is used only in synthesized arguments for
3532
other context parameters. In that case one can avoid defining a parameter name
3633
and just provide its type. Example:
3734
```scala
38-
def maximum[T](xs: List[T]) with Ord[T] : T =
35+
def maximum[T](xs: List[T])(using Ord[T]): T =
3936
xs.reduceLeft(max)
4037
```
4138
`maximum` takes a context parameter of type `Ord` only to pass it on as an
4239
inferred argument to `max`. The name of the parameter is left out.
4340

44-
Generally, context parameters may be defined either as a full parameter list `(p_1: T_1, ..., p_n: T_n)` or just as a sequence of types `T_1, ..., T_n`. Vararg parameters are not supported in with clauses.
45-
46-
**Note:** According to the rules above, a `with` clause like `(A, B)` consisting of types in parentheses defines a context parameter of tuple type. But by analogy with `(a: A, b: B)` one might assume that it defines two context parameters
47-
of type `A` and `B` instead. To avoid confusion, the compiler could issue a warning in this case that clarifies the meaning. If a context parameter of tuple type is in fact intended, the warning can be avoided by switching to a named context parameter,
48-
e.g. `(ab: (A, B))` or enclosing the tuple in an extra set of parentheses, e.g. `((A, B))`.
41+
Generally, context parameters may be defined either as a full parameter list `(p_1: T_1, ..., p_n: T_n)` or just as a sequence of types `T_1, ..., T_n`. Vararg parameters are not supported in using clauses.
4942

5043
## Inferring Complex Arguments
5144

5245
Here are two other methods that have a context parameter of type `Ord[T]`:
5346
```scala
54-
def descending[T] with (asc: Ord[T]) : Ord[T] = new Ord[T] {
47+
def descending[T](using asc: Ord[T]): Ord[T] = new Ord[T] {
5548
def compare(x: T, y: T) = asc.compare(y, x)
5649
}
5750

58-
def minimum[T](xs: List[T]) with Ord[T] =
59-
maximum(xs).with(descending)
51+
def minimum[T](xs: List[T])(using Ord[T]) =
52+
maximum(xs)(using descending)
6053
```
6154
The `minimum` method's right hand side passes `descending` as an explicit argument to `maximum(xs)`.
6255
With this setup, the following calls are all well-formed, and they all normalize to the last one:
6356
```scala
6457
minimum(xs)
65-
maximum(xs).with(descending)
66-
maximum(xs).with(descending.with(listOrd))
67-
maximum(xs).with(descending.with(listOrd.with(intOrd)))
58+
maximum(xs)(using descending)
59+
maximum(xs)(using descending(using listOrd))
60+
maximum(xs)(using descending(using listOrd(using intOrd)))
6861
```
6962

7063
## Multiple With Clauses
7164

72-
There can be several `with` clauses in a definition. Example:
65+
There can be several using clauses in a definition and using clauses can be freely mixed with normal parameter clauses. Example:
7366
```scala
74-
def f(u: Universe) with (ctx: u.Context) with (s: ctx.Symbol, k: ctx.Kind) = ...
67+
def f(u: Universe)(using ctx: u.Context)(using s: ctx.Symbol, k: ctx.Kind) = ...
7568
```
76-
Multiple with clauses are matched left-to-right in applications. Example:
69+
Multiple using clauses are matched left-to-right in applications. Example:
7770
```scala
7871
object global extends Universe { type Context = ... }
7972
given ctx as global.Context { type Symbol = ...; type Kind = ... }
@@ -83,56 +76,31 @@ given kind as ctx.Kind
8376
Then the following calls are all valid (and normalize to the last one)
8477
```scala
8578
f(global)
86-
f(global).with(ctx)
87-
f(global).with(ctx).with(sym, kind)
79+
f(global)(using ctx)
80+
f(global)(using ctx)(using sym, kind)
8881
```
89-
But `f(global).with(sym, kind)` would give a type error.
82+
But `f(global)(using sym, kind)` would give a type error.
9083

91-
`with` clauses can be freely interspersed with normal parameters, but a normal parameter clause cannot
92-
directly follow a `with` clause consisting only of types outside parentheses. So the following is illegal:
93-
```scala
94-
def f with A, B (x: C) = ...
95-
```
96-
But the following variants are valid:
97-
```scala
98-
def g with A, B with (x: C) = ...
99-
def h with (A, B) (x: C) = ...
100-
```
10184

10285
## Summoning Instances
10386

10487
The method `summon` in `Predef` returns the given of a specific type. For example,
10588
the given instance for `Ord[List[Int]]` is produced by
10689
```scala
107-
summon[Ord[List[Int]]] // reduces to listOrd.with(intOrd)
90+
summon[Ord[List[Int]]] // reduces to listOrd(using intOrd)
10891
```
10992
The `summon` method is simply defined as the (non-widening) identity function over a context parameter.
11093
```scala
111-
def summon[T] with (x: T): x.type = x
94+
def summon[T](using x: T): x.type = x
11295
```
11396

11497
## Syntax
11598

116-
Here is the new syntax of parameters and arguments seen as a delta from the [standard context free syntax of Scala 3](../../internals/syntax.md).
99+
Here is the new syntax of parameters and arguments seen as a delta from the [standard context free syntax of Scala 3](../../internals/syntax.md). `using` is a soft keyword, recognized only at the start of a parameter or argument list. It can be used as a normal identifier everywhere else.
117100
```
118-
ClsParamClauses ::= ...
119-
| ClsParamClause ClsParamClauses
120-
| ClsParamClauses1
121-
ClsParamClauses1 ::= WithClsParamClause ClsParamClauses
122-
| AnnotTypes ClsParamClauses1
123-
DefParamClauses ::= ...
124-
| DefParamClause DefParamClauses
125-
| DefParamClauses1
126-
DefParamClauses1 ::= WithParamClause DefParamClauses
127-
| AnnotTypes DefParamClauses1
128-
WithClsParamClause ::= ‘with’ (‘(’ (ClsParams | Types) ‘)’ | AnnotTypes)
129-
WithParamClause ::= ‘with’ (‘(’ (DefParams | Types) ‘)’ | AnnotTypes)
130-
Types ::= Type {‘,’ Type}
131-
AnnotTypes ::= AnnotType {‘,’ AnnotType}
132-
133-
SimpleExpr ::= ...
134-
| SimpleExpr ContextArguments
135-
ParArgumentExprss ::= {ParArgumentExprs | ContextArguments}
136-
ContextArguments ::= ‘.’ ‘with’ ArgumentExprs
137-
101+
ClsParamClause ::= ... | UsingClsParamClause
102+
DefParamClauses ::= ... | UsingParamClause
103+
UsingClsParamClause ::= ‘(’ ‘using’ (ClsParams | Types) ‘)’
104+
UsingParamClause ::= ‘(’ ‘using’ (DefParams | Types) ‘)’
105+
ParArgumentExprs ::= ... | ‘(’ ‘using’ ExprsInParens ‘)’
138106
```

docs/docs/reference/contextual/derivation-new.md

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -139,15 +139,15 @@ signature and implementation of a `derived` method for a type class `TC[_]` are
139139
following form,
140140

141141
```scala
142-
def derived[T] with Mirror.Of[T] : TC[T] = ...
142+
def derived[T](using Mirror.Of[T]): TC[T] = ...
143143
```
144144

145145
That is, the `derived` method takes a context parameter of (some subtype of) type `Mirror` which defines the shape of
146146
the deriving type `T`, and computes the type class implementation according to that shape. This is all that the
147147
provider of an ADT with a `derives` clause has to know about the derivation of a type class instance.
148148

149-
Note that `derived` methods may have given `Mirror` arguments indirectly (e.g. by having a context argument which in turn
150-
has a context `Mirror`, or not at all (e.g. they might use some completely different user-provided mechanism, for
149+
Note that `derived` methods may have context `Mirror` parameters indirectly (e.g. by having a context argument which in turn
150+
has a context `Mirror` parameter, or not at all (e.g. they might use some completely different user-provided mechanism, for
151151
instance using Dotty macros or runtime reflection). We expect that (direct or indirect) `Mirror` based implementations
152152
will be the most common and that is what this document emphasises.
153153

@@ -175,7 +175,7 @@ we need to implement a method `Eq.derived` on the companion object of `Eq` that
175175
a `Mirror[T]`. Here is a possible implementation,
176176

177177
```scala
178-
inline given derived[T] with (m: Mirror.Of[T]) as Eq[T] = {
178+
inline given derived[T](using m: Mirror.Of[T]) as Eq[T] = {
179179
val elemInstances = summonAll[m.MirroredElemTypes] // (1)
180180
inline m match { // (2)
181181
case s: Mirror.SumOf[T] => eqSum(s, elemInstances)
@@ -281,7 +281,7 @@ object Eq {
281281
}
282282
}
283283

284-
inline given derived[T] with (m: Mirror.Of[T]) as Eq[T] = {
284+
inline given derived[T](using m: Mirror.Of[T]) as Eq[T] = {
285285
val elemInstances = summonAll[m.MirroredElemTypes]
286286
inline m match {
287287
case s: Mirror.SumOf[T] => eqSum(s, elemInstances)
@@ -312,7 +312,7 @@ In this case the code that is generated by the inline expansion for the derived
312312
following, after a little polishing,
313313

314314
```scala
315-
given derived$Eq[T] with (eqT: Eq[T]) as Eq[Opt[T]] =
315+
given derived$Eq[T](using eqT: Eq[T]) as Eq[Opt[T]] =
316316
eqSum(summon[Mirror[Opt[T]]],
317317
List(
318318
eqProduct(summon[Mirror[Sm[T]]], List(summon[Eq[T]]))
@@ -329,24 +329,27 @@ As a third example, using a higher level library such as shapeless the type clas
329329
`derived` method as,
330330

331331
```scala
332-
given eqSum[A] with (inst: => K0.CoproductInstances[Eq, A]) as Eq[A] {
332+
given eqSum[A](using inst: => K0.CoproductInstances[Eq, A]) as Eq[A] {
333333
def eqv(x: A, y: A): Boolean = inst.fold2(x, y)(false)(
334334
[t] => (eqt: Eq[t], t0: t, t1: t) => eqt.eqv(t0, t1)
335335
)
336336
}
337337

338-
given eqProduct[A] with (inst: K0.ProductInstances[Eq, A]) as Eq[A] {
338+
given eqProduct[A](using inst: K0.ProductInstances[Eq, A]) as Eq[A] {
339339
def eqv(x: A, y: A): Boolean = inst.foldLeft2(x, y)(true: Boolean)(
340340
[t] => (acc: Boolean, eqt: Eq[t], t0: t, t1: t) => Complete(!eqt.eqv(t0, t1))(false)(true)
341341
)
342342
}
343343

344-
345-
inline def derived[A] with (gen: K0.Generic[A]) as Eq[A] = gen.derive(eqSum, eqProduct)
344+
inline def derived[A](using gen: K0.Generic[A]) as Eq[A] = gen.derive(eqSum, eqProduct)
346345
```
347346

348347
The framework described here enables all three of these approaches without mandating any of them.
349348

349+
For a brief discussion on how to use macros to write a type class `derived`
350+
method please read more at [How to write a type class `derived` method using
351+
macros](./derivation-macro.md).
352+
350353
### Deriving instances elsewhere
351354

352355
Sometimes one would like to derive a type class instance for an ADT after the ADT is defined, without being able to

0 commit comments

Comments
 (0)