Skip to content

Commit 432dc3d

Browse files
committed
doc(extension method): revise examples
1 parent ff476f7 commit 432dc3d

File tree

1 file changed

+66
-23
lines changed

1 file changed

+66
-23
lines changed

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

Lines changed: 66 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@ circle.circumference
2121
### Translation of Extension Methods
2222

2323
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:
24+
identifier. They translate to functions where the leading parameter list is
25+
either turned into the first argument list of the function or
26+
into the last argument list in case of a right-associative identifier.
27+
So, the definition of `circumference` above translates
28+
to the following function, and can also be invoked as such:
2729
```scala
2830
def circumference(c: Circle): Double = c.radius * math.Pi * 2
2931

@@ -32,37 +34,68 @@ assert(circle.circumference == circumference(circle))
3234

3335
### Translation of Calls to Extension Methods
3436

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

37-
- An extension method is applicable if it is visible under a simple name, by being defined
39+
1. An extension method is applicable if it is visible, by being defined
3840
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.
41+
2. An extension method is applicable if it is a member of some given instance at the point of the application.
4042

41-
As an example, consider an extension method `longestStrings` on `Seq[String]` defined in a trait `StringSeqOps`.
43+
Following an example for the first point:
4244

4345
```scala
44-
trait StringSeqOps {
45-
def (xs: Seq[String]).longestStrings = {
46-
val maxLength = xs.map(_.length).max
47-
xs.filter(_.length == maxLength)
48-
}
46+
trait IntOps {
47+
def (i: Int).isZero: Boolean = i == 0
48+
49+
def (i: Int).safeMod(x: Int): Option[Int] =
50+
// extension method defined in same scope IntOps
51+
if x.isZero then None
52+
else Some(i % x)
53+
}
54+
55+
object IntOpsEx extends IntOps {
56+
def (i: Int).safeDiv(x: Int): Option[Int] =
57+
// extension method brought into scope via inheritance from IntOps
58+
if x.isZero then None
59+
else Some(i % x)
60+
}
61+
62+
trait SafeDiv {
63+
import IntOpsEx._
64+
65+
def divide(i: Int, d: Int) : Option[(Int, Int)] =
66+
// extension method imported and thus in scope
67+
(i.safeDiv(d), i.safeMod(d)) match {
68+
case (Some(d), Some(r)) => Some((d, r))
69+
case _ => None
70+
}
4971
}
5072
```
51-
We can make the extension method available by defining a given `StringSeqOps` instance, like this:
73+
74+
We build up on the above example to outline the second point.
75+
We can make an extension method available by defining a given instance containing it, like this:
5276
```scala
53-
given ops1 as StringSeqOps
77+
given ops1 as IntOps // brings safeMod into scope
78+
79+
1.safeMod(2)
5480
```
55-
Then
81+
82+
Then `safeMod` is legal everywhere `ops1` is available. Anonymous givens (and any other form of givens) are supported as well:
5683
```scala
57-
List("here", "is", "a", "list").longestStrings
84+
given SafeDiv //brings safeMod, safeDiv and divide into scope
5885
```
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.
6086

87+
In case the extension method is defined in an object as in `IntOpsEx`, then it can also be brought into scope by a given:
88+
```scala
89+
import given IntOpsEx.type = IntOpsEx // unusual, brings safeMod and safeDiv into scope
90+
```
91+
However importing from an object is most likely the better choice. And also superior regarding selectivety:
6192
```scala
62-
object ops2 extends StringSeqOps
63-
import ops2.longestStrings
64-
List("here", "is", "a", "list").longestStrings
93+
import IntOpsEx.safeMod // brings only safeMod into scope
94+
95+
1.safeMod(2)
96+
2.safeDiv(3) // compile error
6597
```
98+
6699
The precise rules for resolving a selection to an extension method are as follows.
67100

68101
Assume a selection `e.m[Ts]` where `m` is not a member of `e`, where the type arguments `[Ts]` are optional,
@@ -94,20 +127,28 @@ x min 3
94127
```
95128
For alphanumeric extension operators like `min` an `@infix` annotation is implied.
96129

130+
<!--
131+
TODO: what about @alpha for the non alphanumeric operators, should be required according to
132+
http://dotty.epfl.ch/docs/reference/changed-features/operators.html
133+
"Symbolic methods without @alpha annotations are deprecated"
134+
-->
135+
97136
The three definitions above translate to
98137
```scala
99138
def < (x: String)(y: String) = ...
100139
def +: (xs: Seq[Elem])(x: Elem) = ...
101140
def min(x: Number)(y: Number) = ...
102141
```
103142
Note the swap of the two parameters `x` and `xs` when translating
104-
the right-binding operator `+:` to an extension method. This is analogous
143+
the right-associative operator `+:` to an extension method. This is analogous
105144
to the implementation of right binding operators as normal methods.
106145

107146

108147
### Generic Extensions
109148

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:
149+
The `IntOps` examples extended a non generic type.
150+
It is also possible to extend a specific instance of a generic type (e.g. Seq[String] -- see `stringOps` further below).
151+
And also generic types by adding type parameters to an extension method. Examples:
111152

112153
```scala
113154
def [T](xs: List[T]) second =
@@ -120,10 +161,12 @@ def [T: Numeric](x: T) + (y: T): T =
120161
summon[Numeric[T]].plus(x, y)
121162
```
122163

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:
164+
If an extension method has type parameters, they come immediately after the `def` and are followed by the extended parameter.
165+
When calling a generic extension method, any explicitly given type arguments follow the method name. So the `second` method can be instantiated as follows:
124166
```scala
125167
List(1, 2, 3).second[Int]
126168
```
169+
(it's only a showcase, the compiler could of course infer the type).
127170

128171
### Extension Instances
129172

0 commit comments

Comments
 (0)