Skip to content

Commit 03aa171

Browse files
Merge pull request #9362 from ShapelessCat/fix-docs-of-contextual-abstractions
Fix docs of "contextual abstractions"
2 parents cfc1c69 + 849484d commit 03aa171

File tree

4 files changed

+114
-49
lines changed

4 files changed

+114
-49
lines changed

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

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,31 @@ layout: doc-page
33
title: "Context Bounds"
44
---
55

6-
## Context Bounds
7-
86
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:
7+
98
```scala
109
def maximum[T: Ord](xs: List[T]): T = xs.reduceLeft(max)
1110
```
11+
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.,
13+
1314
```scala
1415
def f[T: C1 : C2, U: C3](x: T)(using y: U, z: V): R
1516
```
17+
1618
would expand to
19+
1720
```scala
1821
def f[T, U](x: T)(using y: U, z: V)(using C1[T], C2[T], C3[U]): R
1922
```
23+
2024
Context bounds can be combined with subtype bounds. If both are present, subtype bounds come first, e.g.
25+
2126
```scala
2227
def g[T <: B : C](x: T): R = ...
2328
```
2429

25-
### Migration
30+
## Migration
2631

2732
To ease migration, context bounds in Dotty map in Scala 3.0 to old-style implicit parameters
2833
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.
@@ -32,7 +37,7 @@ context parameter stemming from a context bound with a normal argument will give
3237
warning. The warning indicates that a `(using ...)` clause is needed instead. The rewrite can be
3338
done automatically under `-rewrite`.
3439

35-
### Syntax
40+
## Syntax
3641

3742
```
3843
TypeParamBounds ::= [SubtypeBounds] {ContextBound}

docs/docs/reference/contextual/extension-methods.md

Lines changed: 49 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,11 @@ def extension_circumference(c: Circle): Double = c.radius * math.Pi * 2
3030

3131
assert(circle.circumference == extension_circumference(circle))
3232
```
33+
3334
### Operators
3435

3536
The extension method syntax can also be used to define operators. Examples:
37+
3638
```scala
3739
extension (x: String)
3840
def < (y: String): Boolean = ...
@@ -47,11 +49,13 @@ x min 3
4749
```
4850

4951
The three definitions above translate to
52+
5053
```scala
5154
def extension_< (x: String)(y: String): Boolean = ...
5255
def extension_+: (xs: Seq[Elem])(x: Elem): Seq[Elem] = ...
5356
@infix def extension_min(x: Number)(y: Number): Number = ...
5457
```
58+
5559
Note the swap of the two parameters `x` and `xs` when translating
5660
the right-associative operator `+:` to an extension method. This is analogous
5761
to the implementation of right binding operators as normal methods. The Scala
@@ -61,28 +65,30 @@ the two swaps cancel each other out).
6165

6266
### Generic Extensions
6367

64-
It is also possible to extend generic types by adding type parameters to an extension. For instance:
68+
It is also possible to extend generic types by adding type parameters to an extension. For instance:
6569

66-
```scala
67-
extension [T](xs: List[T])
68-
def second = xs.tail.head
70+
```scala
71+
extension [T](xs: List[T])
72+
def second = xs.tail.head
6973

70-
extension [T: Numeric](x: T)
71-
def + (y: T): T = summon[Numeric[T]].plus(x, y)
74+
extension [T: Numeric](x: T)
75+
def + (y: T): T = summon[Numeric[T]].plus(x, y)
7276
```
7377

7478
If an extension method has type parameters, they come immediately after `extension` and are followed by the extended parameter.
7579
When calling a generic extension method, any explicitly given type arguments follow the method name. So the `second` method could be instantiated as follows.
80+
7681
```scala
77-
List(1, 2, 3).second[Int]
82+
List(1, 2, 3).second[Int]
7883
```
79-
Of course, the type argument here would usually be left out since it can be inferred.
8084

85+
Of course, the type argument here would usually be left out since it can be inferred.
8186

8287
Extensions can also take using clauses. For instance, the `+` extension above could equivalently be written with a using clause:
88+
8389
```scala
84-
extension [T](x: T)(using n: Numeric[T])
85-
def - (y: T): T = n.minus(x, y)
90+
extension [T](x: T)(using n: Numeric[T])
91+
def + (y: T): T = n.plus(x, y)
8692
```
8793

8894
**Note**: Type parameters have to be given after the `extension` keyword;
@@ -94,6 +100,7 @@ Sometimes, one wants to define several extension methods that share the same
94100
left-hand parameter type. In this case one can "pull out" the common parameters into
95101
a single extension and enclose all methods in braces or an indented region following a '`:`'.
96102
Example:
103+
97104
```scala
98105
extension (ss: Seq[String])
99106

@@ -109,6 +116,7 @@ assuming the common extended value `ss` as receiver.
109116

110117
Collective extensions like these are a shorthand for individual extensions
111118
where each method is defined separately. For instance, the first extension above expands to
119+
112120
```scala
113121
extension (ss: Seq[String])
114122
def longestStrings: Seq[String] =
@@ -118,7 +126,9 @@ extension (ss: Seq[String])
118126
extension (ss: Seq[String])
119127
def longestString: String = ss.longestStrings.head
120128
```
129+
121130
Collective extensions also can take type parameters and have using clauses. Example
131+
122132
```scala
123133
extension [T](xs: List[T])(using Ordering[T])
124134
def smallest(n: Int): List[T] = xs.sorted.take(n)
@@ -167,14 +177,17 @@ trait SafeDiv:
167177
case (Some(d), Some(r)) => Some((d, r))
168178
case _ => None
169179
```
180+
170181
By the second rule, an extension method can be made available by defining a given instance containing it, like this:
182+
171183
```scala
172184
given ops1 as IntOps // brings safeMod into scope
173185

174186
1.safeMod(2)
175187
```
176188

177189
By the third and fourth rule, an extension method is available if it is in the implicit scope of the receiver type or in a given instance in that scope. Example:
190+
178191
```scala
179192
class List[T]:
180193
...
@@ -204,53 +217,58 @@ Assume a selection `e.m[Ts]` where `m` is not a member of `e`, where the type ar
204217
2. If the first rewriting does not typecheck with expected type `T`,
205218
and there is an extension method `m` in some eligible object `o`, the selection is rewritten to `o.extension_m[Ts](e)`. An object `o` is _eligible_ if
206219

207-
- `o` forms part of the implicit scope of `T`, or
208-
- `o` is a given instance that is visible at the point of the application, or
209-
- `o` is a given instance in the implicit scope of `T`.
220+
- `o` forms part of the implicit scope of `T`, or
221+
- `o` is a given instance that is visible at the point of the application, or
222+
- `o` is a given instance in the implicit scope of `T`.
210223

211224
This second rewriting is attempted at the time where the compiler also tries an implicit conversion
212225
from `T` to a type containing `m`. If there is more than one way of rewriting, an ambiguity error results.
213226

214227
An extension method can also be used as an identifier by itself. If an identifier `m` does not
215228
resolve, the identifier is rewritten to:
216229

217-
- `x.m` if the identifier appears in an extension with parameter `x`
218-
- `this.m` otherwise
230+
- `x.m` if the identifier appears in an extension with parameter `x`
231+
- `this.m` otherwise
219232

220233
and the rewritten term is again tried as an application of an extension method. Example:
234+
221235
```scala
222-
extension (s: String)
223-
def position(ch: Char, n: Int): Int =
224-
if n < s.length && s(n) != ch then position(ch, n + 1)
225-
else n
236+
extension (s: String)
237+
def position(ch: Char, n: Int): Int =
238+
if n < s.length && s(n) != ch then position(ch, n + 1)
239+
else n
226240
```
241+
227242
The recursive call `position(ch, n + 1)` expands to `s.position(ch, n + 1)` in this case. The whole extension method rewrites to
243+
228244
```scala
229245
def extension_position(s: String)(ch: Char, n: Int): Int =
230246
if n < s.length && s(n) != ch then extension_position(s)(ch, n + 1)
231247
else n
232248
```
249+
233250
### More Details
234251

235252
1. To avoid confusion, names of normal methods are not allowed to start with `extension_`.
236253

237-
2. A named import such as `import a.m` of an extension method in `a` will make `m`
238-
only available as an extension method. To access it under
239-
`extension_m` that name as to be imported separately. Example:
240-
```scala
241-
object DoubleOps:
242-
extension (x: Double) def ** (exponent: Int): Double =
243-
require(exponent > 0)
244-
if exponent == 0 then 1 else x * (x ** (exponent - 1))
254+
2. A named import such as `import a.m` of an extension method in `a` will make `m` only available as an extension method.
255+
To access it under `extension_m` that name as to be imported separately. Example:
245256

246-
import DoubleOps.{**, extension_**}
247-
assert(2.0 ** 3 == extension_**(2.0)(3))
248-
```
257+
```scala
258+
object DoubleOps:
259+
extension (x: Double) def ** (exponent: Int): Double =
260+
require(exponent >= 0)
261+
if exponent == 0 then 1 else x * (x ** (exponent - 1))
262+
263+
import DoubleOps.{**, extension_**}
264+
assert(2.0 ** 3 == extension_**(2.0)(3))
265+
```
249266

250267
### Syntax
251268

252269
Here are the syntax changes for extension methods and collective extensions relative
253270
to the [current syntax](../../internals/syntax.md).
271+
254272
```
255273
BlockStat ::= ... | Extension
256274
TemplateStat ::= ... | Extension
@@ -260,6 +278,7 @@ Extension ::= ‘extension’ [DefTypeParamClause] ‘(’ DefParam ‘
260278
ExtMethods ::= ExtMethod | [nl] ‘{’ ExtMethod {semi ExtMethod ‘}’
261279
ExtMethod ::= {Annotation [nl]} {Modifier} ‘def’ DefDef
262280
```
281+
263282
`extension` is a soft keyword. It is recognized as a keyword only if it appears
264283
at the start of a statement and is followed by `[` or `(`. In all other cases
265284
it is treated as an identifier.

0 commit comments

Comments
 (0)