Skip to content

Commit 24672e6

Browse files
authored
Merge pull request #8508 from tegonal/doc-extension-method
doc(extension method): revise examples
2 parents 588ac1b + 17f1414 commit 24672e6

File tree

1 file changed

+58
-27
lines changed

1 file changed

+58
-27
lines changed

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

Lines changed: 58 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ circle.circumference
2020

2121
### Translation of Extension Methods
2222

23-
Extension methods are methods that have a parameter clause in front of the defined
24-
identifier. They translate to methods where the leading parameter section is moved
25-
to after the defined identifier. So, the definition of `circumference` above translates
26-
to the plain method, and can also be invoked as such:
23+
Extension methods are methods that have a parameter clause in front of the defined identifier.
24+
They translate to functions where the leading parameter section is turned into the first argument list of the function.
25+
So, the definition of `circumference` above translates to the following function, and can also be invoked as such:
26+
2727
```scala
2828
def circumference(c: Circle): Double = c.radius * math.Pi * 2
2929

@@ -32,37 +32,58 @@ assert(circle.circumference == circumference(circle))
3232

3333
### Translation of Calls to Extension Methods
3434

35-
When is an extension method applicable? There are two possibilities.
35+
When is an extension method applicable? There are two possibilities:
3636

37-
- An extension method is applicable if it is visible under a simple name, by being defined
37+
1. An extension method is applicable if it is visible under a simple name, by being defined
3838
or inherited or imported in a scope enclosing the application.
39-
- An extension method is applicable if it is a member of some given instance at the point of the application.
39+
2. An extension method is applicable if it is a member of some given instance at the point of the application.
4040

41-
As an example, consider an extension method `longestStrings` on `Seq[String]` defined in a trait `StringSeqOps`.
41+
Here is an example for the first rule:
4242

4343
```scala
44-
trait StringSeqOps {
45-
def (xs: Seq[String]).longestStrings = {
46-
val maxLength = xs.map(_.length).max
47-
xs.filter(_.length == maxLength)
48-
}
44+
trait IntOps {
45+
def (i: Int).isZero: Boolean = i == 0
46+
47+
def (i: Int).safeMod(x: Int): Option[Int] =
48+
// extension method defined in same scope IntOps
49+
if x.isZero then None
50+
else Some(i % x)
51+
}
52+
53+
object IntOpsEx extends IntOps {
54+
def (i: Int).safeDiv(x: Int): Option[Int] =
55+
// extension method brought into scope via inheritance from IntOps
56+
if x.isZero then None
57+
else Some(i % x)
58+
}
59+
60+
trait SafeDiv {
61+
import IntOpsEx._ // brings safeDiv and safeMod into scope
62+
63+
def (i: Int) divide(d: Int) : Option[(Int, Int)] =
64+
// extension methods imported and thus in scope
65+
(i.safeDiv(d), i.safeMod(d)) match {
66+
case (Some(d), Some(r)) => Some((d, r))
67+
case _ => None
68+
}
4969
}
5070
```
51-
We can make the extension method available by defining a given `StringSeqOps` instance, like this:
52-
```scala
53-
given ops1 as StringSeqOps
54-
```
55-
Then
71+
72+
We build up on the above example to outline the second point.
73+
We can make an extension method available by defining a given instance containing it, like this:
5674
```scala
57-
List("here", "is", "a", "list").longestStrings
75+
given ops1 as IntOps // brings safeMod into scope
76+
77+
1.safeMod(2)
5878
```
59-
is legal everywhere `ops1` is available. Alternatively, we can define `longestStrings` as a member of a normal object. But then the method has to be brought into scope to be usable as an extension method.
6079

80+
Then `safeMod` is legal everywhere `ops1` is available. Anonymous givens (and any other form of givens) are supported as well:
6181
```scala
62-
object ops2 extends StringSeqOps
63-
import ops2.longestStrings
64-
List("here", "is", "a", "list").longestStrings
82+
given SafeDiv //brings divide into scope (safeMod and safeDiv are not automatically exported)
83+
84+
1.divide(2)
6585
```
86+
6687
The precise rules for resolving a selection to an extension method are as follows.
6788

6889
Assume a selection `e.m[Ts]` where `m` is not a member of `e`, where the type arguments `[Ts]` are optional,
@@ -94,20 +115,28 @@ x min 3
94115
```
95116
For alphanumeric extension operators like `min` an `@infix` annotation is implied.
96117

118+
<!--
119+
TODO: what about @alpha for the non alphanumeric operators, should be required according to
120+
http://dotty.epfl.ch/docs/reference/changed-features/operators.html
121+
"Symbolic methods without @alpha annotations are deprecated"
122+
-->
123+
97124
The three definitions above translate to
98125
```scala
99126
def < (x: String)(y: String) = ...
100127
def +: (xs: Seq[Elem])(x: Elem) = ...
101128
def min(x: Number)(y: Number) = ...
102129
```
103130
Note the swap of the two parameters `x` and `xs` when translating
104-
the right-binding operator `+:` to an extension method. This is analogous
131+
the right-associative operator `+:` to an extension method. This is analogous
105132
to the implementation of right binding operators as normal methods.
106133

107134

108135
### Generic Extensions
109136

110-
The `StringSeqOps` examples extended a specific instance of a generic type. It is also possible to extend a generic type by adding type parameters to an extension method. Examples:
137+
The `IntOps` examples extended a non generic type.
138+
It is also possible to extend a specific instance of a generic type (e.g. Seq[String] -- see `stringOps` further below).
139+
Moreover, it is also possible to extend generic types by adding type parameters to an extension method. Examples:
111140

112141
```scala
113142
def [T](xs: List[T]) second =
@@ -120,10 +149,12 @@ def [T: Numeric](x: T) + (y: T): T =
120149
summon[Numeric[T]].plus(x, y)
121150
```
122151

123-
If an extension method has type parameters, they come immediately after the `def` and are followed by the extended parameter. When calling a generic extension method, any explicitly given type arguments follow the method name. So the `second` method can be instantiated as follows:
152+
If an extension method has type parameters, they come immediately after the `def` and are followed by the extended parameter.
153+
When calling a generic extension method, any explicitly given type arguments follow the method name. So the `second` method can be instantiated as follows:
124154
```scala
125155
List(1, 2, 3).second[Int]
126156
```
157+
(it's only a showcase, the compiler could of course infer the type).
127158

128159
### Extension Instances
129160

@@ -257,4 +288,4 @@ extension on ...
257288
extension <ident> on ...
258289
extension { ...
259290
extension <ident> { ...
260-
```
291+
```

0 commit comments

Comments
 (0)