Skip to content

Commit 4ca6ab6

Browse files
committed
Update doc page
1 parent ea57ab8 commit 4ca6ab6

File tree

2 files changed

+30
-39
lines changed

2 files changed

+30
-39
lines changed

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

Lines changed: 29 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,13 @@ circle.circumference
2121

2222
### Translation of Extension Methods
2323

24-
Extension methods are methods that have a parameter clause in front of the defined identifier.
25-
An extension method named `f` translates to the method named `extension_f` that takes the leading parameter section as its first argument list.
26-
So, the definition of `circumference` above translates to the following method, and can also be invoked as such:
24+
An extension method translates to a specially labelled method that takes the leading parameter section as its first argument list. The label, expressed
25+
as `<extension>` here, is compiler-internal. So, the definition of `circumference` above translates to the following method, and can also be invoked as such:
2726

2827
```scala
29-
def extension_circumference(c: Circle): Double = c.radius * math.Pi * 2
28+
<extension> def circumference(c: Circle): Double = c.radius * math.Pi * 2
3029

31-
assert(circle.circumference == extension_circumference(circle))
30+
assert(circle.circumference == circumference(circle))
3231
```
3332

3433
### Operators
@@ -51,9 +50,9 @@ x min 3
5150
The three definitions above translate to
5251

5352
```scala
54-
def extension_< (x: String)(y: String): Boolean = ...
55-
def extension_+: (xs: Seq[Elem])(x: Elem): Seq[Elem] = ...
56-
@infix def extension_min(x: Number)(y: Number): Number = ...
53+
<extension> def < (x: String)(y: String): Boolean = ...
54+
<extension> def +: (xs: Seq[Elem])(x: Elem): Seq[Elem] = ...
55+
@infix <extension> def min(x: Number)(y: Number): Number = ...
5756
```
5857

5958
Note the swap of the two parameters `x` and `xs` when translating
@@ -101,7 +100,7 @@ By contrast, using clauses can be defined for the `extension` as well as per `de
101100
Sometimes, one wants to define several extension methods that share the same
102101
left-hand parameter type. In this case one can "pull out" the common parameters into
103102
a single extension and enclose all methods in braces or an indented region following a '`:`'.
104-
Following an example using an indented region:
103+
Example:
105104

106105
```scala
107106
extension (ss: Seq[String]):
@@ -141,6 +140,16 @@ extension (ss: Seq[String])
141140
def longestString: String = ss.longestStrings.head
142141
```
143142

143+
Collective extensions also can take type parameters and have using clauses. Example:
144+
145+
```scala
146+
extension [T](xs: List[T])(using Ordering[T]):
147+
def smallest(n: Int): List[T] = xs.sorted.take(n)
148+
def smallestIndices(n: Int): List[Int] =
149+
val limit = smallest(n).max
150+
xs.zipWithIndex.collect { case (x, i) if x <= limit => i }
151+
```
152+
144153
### Translation of Calls to Extension Methods
145154

146155
To convert a reference to an extension method, the compiler has to know about the extension
@@ -218,9 +227,9 @@ The precise rules for resolving a selection to an extension method are as follow
218227
Assume a selection `e.m[Ts]` where `m` is not a member of `e`, where the type arguments `[Ts]` are optional, and where `T` is the expected type.
219228
The following two rewritings are tried in order:
220229

221-
1. The selection is rewritten to `extension_m[Ts](e)`.
230+
1. The selection is rewritten to m[Ts](e)`.
222231
2. If the first rewriting does not typecheck with expected type `T`,
223-
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
232+
and there is an extension method `m` in some eligible object `o`, the selection is rewritten to `o.m[Ts](e)`. An object `o` is _eligible_ if
224233

225234
- `o` forms part of the implicit scope of `T`, or
226235
- `o` is a given instance that is visible at the point of the application, or
@@ -229,15 +238,13 @@ The following two rewritings are tried in order:
229238
This second rewriting is attempted at the time where the compiler also tries an implicit conversion
230239
from `T` to a type containing `m`. If there is more than one way of rewriting, an ambiguity error results.
231240

232-
An extension method can also be used as an identifier by itself. If an identifier `m` does not
233-
resolve, the identifier is rewritten to:
234-
235-
- `x.m` if the identifier appears in an extension with parameter `x`
236-
and the method `m` resolves to an extension method in
237-
a (possibly collective) extension that also contains the call,
238-
- `this.m` otherwise
239-
240-
and the rewritten term is again tried as an application of an extension method. In
241+
An extension method can also be referenced using a simple identifier without a preceding expression. If an identifier `g` appears in the body of an extension method `f` and refers to an extension method `g` that is defined in the same collective extension
242+
```scala
243+
extension (x: T)
244+
def f ... = ... g ...
245+
def g ...
246+
```
247+
the identifier is rewritten to `x.g`. This is also the case if `f` and `g` are the same method. Example:
241248

242249
```scala
243250
extension (s: String)
@@ -249,27 +256,11 @@ extension (s: String)
249256
The recursive call `position(ch, n + 1)` expands to `s.position(ch, n + 1)` in this case. The whole extension method rewrites to
250257

251258
```scala
252-
def extension_position(s: String)(ch: Char, n: Int): Int =
253-
if n < s.length && s(n) != ch then extension_position(s)(ch, n + 1)
259+
def position(s: String)(ch: Char, n: Int): Int =
260+
if n < s.length && s(n) != ch then position(s)(ch, n + 1)
254261
else n
255262
```
256263

257-
### More Details
258-
259-
1. To avoid confusion, names of normal methods are not allowed to start with `extension_`.
260-
261-
2. A named import such as `import a.m` of an extension method in `a` will make `m` only available as an extension method.
262-
To access it under `extension_m` that name has to be imported separately. Example:
263-
264-
```scala
265-
object DoubleOps:
266-
extension (x: Double) def ** (exponent: Int): Double =
267-
require(exponent >= 0)
268-
if exponent == 0 then 1 else x * (x ** (exponent - 1))
269-
270-
import DoubleOps.{**, extension_**}
271-
assert(2.0 ** 3 == extension_**(2.0)(3))
272-
```
273264

274265
### Syntax
275266

tests/pos/reference/extension-methods.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ object ExtMethods:
99

1010
val circle = Circle(0, 0, 1)
1111
circle.circumference
12-
assert(circle.circumference == this.circumference(circle))
12+
assert(circle.circumference == circumference(circle))
1313

1414
extension (x: String) def < (y: String) = x.compareTo(y) < 0
1515
extension [Elem](x: Elem) def #: (xs: Seq[Elem]) = x +: xs

0 commit comments

Comments
 (0)