Skip to content

Commit 6dd4a99

Browse files
committed
Rearrange doc pages
Rearrange doc pages so that we can merge before the next release.
1 parent 14db070 commit 6dd4a99

File tree

2 files changed

+200
-24
lines changed

2 files changed

+200
-24
lines changed
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
---
2+
layout: doc-page
3+
title: "Extension Methods"
4+
---
5+
6+
Extension methods allow one to add methods to a type after the type is defined. Example:
7+
8+
```scala
9+
case class Circle(x: Double, y: Double, radius: Double)
10+
11+
def (c: Circle).circumference: Double = c.radius * math.Pi * 2
12+
```
13+
14+
Like regular methods, extension methods can be invoked with infix `.`:
15+
16+
```scala
17+
val circle = Circle(0, 0, 1)
18+
circle.circumference
19+
```
20+
21+
### Translation of Extension Methods
22+
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:
27+
```scala
28+
def circumference(c: Circle): Double = c.radius * math.Pi * 2
29+
30+
assert(circle.circumference == circumference(circle))
31+
```
32+
33+
### Translation of Calls to Extension Methods
34+
35+
When is an extension method applicable? There are two possibilities.
36+
37+
- An extension method is applicable if it is visible under a simple name, by being defined
38+
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.
40+
41+
As an example, consider an extension method `longestStrings` on `Seq[String]` defined in a trait `StringSeqOps`.
42+
43+
```scala
44+
trait StringSeqOps {
45+
def (xs: Seq[String]).longestStrings = {
46+
val maxLength = xs.map(_.length).max
47+
xs.filter(_.length == maxLength)
48+
}
49+
}
50+
```
51+
We can make the extension method available by defining a given `StringSeqOps` instance, like this:
52+
```scala
53+
given ops1: StringSeqOps
54+
```
55+
Then
56+
```scala
57+
List("here", "is", "a", "list").longestStrings
58+
```
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.
60+
61+
```scala
62+
object ops2 extends StringSeqOps
63+
import ops2.longestStrings
64+
List("here", "is", "a", "list").longestStrings
65+
```
66+
The precise rules for resolving a selection to an extension method are as follows.
67+
68+
Assume a selection `e.m[Ts]` where `m` is not a member of `e`, where the type arguments `[Ts]` are optional,
69+
and where `T` is the expected type. The following two rewritings are tried in order:
70+
71+
1. The selection is rewritten to `m[Ts](e)`.
72+
2. If the first rewriting does not typecheck with expected type `T`, and there is a given instance `i`
73+
in either the current scope or in the implicit scope of `T`, and `i` defines an extension
74+
method named `m`, then selection is expanded to `i.m[Ts](e)`.
75+
This second rewriting is attempted at the time where the compiler also tries an implicit conversion
76+
from `T` to a type containing `m`. If there is more than one way of rewriting, an ambiguity error results.
77+
78+
So `circle.circumference` translates to `CircleOps.circumference(circle)`, provided
79+
`circle` has type `Circle` and `CircleOps` is given (i.e. it is visible at the point of call or it is defined in the companion object of `Circle`).
80+
81+
### Operators
82+
83+
The extension method syntax also applies to the definition of operators.
84+
In this case it is allowed and preferable to omit the period between the leading parameter list
85+
and the operator. In each case the definition syntax mirrors the way the operator is applied.
86+
Examples:
87+
```scala
88+
def (x: String) < (y: String) = ...
89+
def (x: Elem) +: (xs: Seq[Elem]) = ...
90+
def (x: Number) min (y: Number) = ...
91+
92+
"ab" < "c"
93+
1 +: List(2, 3)
94+
x min 3
95+
```
96+
The three definitions above translate to
97+
```scala
98+
def < (x: String)(y: String) = ...
99+
def +: (xs: Seq[Elem])(x: Elem) = ...
100+
def min(x: Number)(y: Number) = ...
101+
```
102+
Note the swap of the two parameters `x` and `xs` when translating
103+
the right-binding operator `+:` to an extension method. This is analogous
104+
to the implementation of right binding operators as normal methods.
105+
106+
### Generic Extensions
107+
108+
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:
109+
110+
```scala
111+
def [T](xs: List[T]) second =
112+
xs.tail.head
113+
114+
def [T](xs: List[List[T]]) flattened =
115+
xs.foldLeft[List[T]](Nil)(_ ++ _)
116+
117+
def [T: Numeric](x: T) + (y: T): T =
118+
summon[Numeric[T]].plus(x, y)
119+
```
120+
121+
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:
122+
```scala
123+
List(1, 2, 3).second[Int]
124+
```
125+
### Collective Extensions
126+
127+
A collective extension defines one or more concrete methods that have the same type parameters
128+
and prefix parameter. Examples:
129+
130+
```scala
131+
extension stringOps of (xs: Seq[String]) with {
132+
def longestStrings: Seq[String] = {
133+
val maxLength = xs.map(_.length).max
134+
xs.filter(_.length == maxLength)
135+
}
136+
}
137+
138+
extension listOps of [T](xs: List[T]) with {
139+
def second = xs.tail.head
140+
def third: T = xs.tail.tail.head
141+
}
142+
143+
extension of [T](xs: List[T])(given Ordering[T]) with {
144+
def largest(n: Int) = xs.sorted.takeRight(n)
145+
}
146+
```
147+
If a given extension is anonymous (as in the last clause), its name is synthesized from the name of the first defined extension method.
148+
149+
The extensions above are equivalent to the following regular given instances where the implemented parent is `AnyRef` and the parameters in the `extension` clause are repeated in each extension method definition:
150+
```scala
151+
given stringOps: AnyRef {
152+
def (xs: Seq[String]).longestStrings: Seq[String] = {
153+
val maxLength = xs.map(_.length).max
154+
xs.filter(_.length == maxLength)
155+
}
156+
}
157+
given listOps: AnyRef {
158+
def [T](xs: List[T]) second = xs.tail.head
159+
def [T](xs: List[T]) third: T = xs.tail.tail.head
160+
}
161+
given extension_largest_List_T: AnyRef {
162+
def [T](xs: List[T]) largest (given Ordering[T])(n: Int) =
163+
xs.sorted.takeRight(n)
164+
}
165+
```
166+
167+
`extension` and `of` are soft keywords. They can also be used as a regular identifiers.
168+
169+
### Syntax
170+
171+
Here are the syntax changes for extension methods and given extensions relative
172+
to the [current syntax](../../internals/syntax.md). `extension` is a soft keyword, recognized only after a `given`. It can be used as an identifier everywhere else.
173+
```
174+
DefSig ::= ...
175+
| ExtParamClause [nl] [‘.’] id DefParamClauses
176+
ExtParamClause ::= [DefTypeParamClause] ‘(’ DefParam ‘)’
177+
TmplDef ::= ...
178+
| ‘extension’ ExtensionDef
179+
ExtensionDef ::= [id] ‘of’ ExtParamClause {GivenParamClause} ‘with’ ExtMethods
180+
ExtMethods ::= ‘{’ ‘def’ DefDef {semi ‘def’ DefDef} ‘}’
181+
```

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

Lines changed: 19 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,15 @@ layout: doc-page
33
title: "Extension Methods"
44
---
55

6+
**Note:** The syntax of extension methods is about to change. Here is the
7+
[doc page with the new syntax](./extension-methods-new.html), supported from Dotty 0.22 onwards.
8+
69
Extension methods allow one to add methods to a type after the type is defined. Example:
710

811
```scala
912
case class Circle(x: Double, y: Double, radius: Double)
1013

11-
def (c: Circle).circumference: Double = c.radius * math.Pi * 2
14+
def (c: Circle) circumference: Double = c.radius * math.Pi * 2
1215
```
1316

1417
Like regular methods, extension methods can be invoked with infix `.`:
@@ -42,7 +45,7 @@ As an example, consider an extension method `longestStrings` on `Seq[String]` de
4245

4346
```scala
4447
trait StringSeqOps {
45-
def (xs: Seq[String]).longestStrings = {
48+
def (xs: Seq[String]) longestStrings = {
4649
val maxLength = xs.map(_.length).max
4750
xs.filter(_.length == maxLength)
4851
}
@@ -81,25 +84,21 @@ So `circle.circumference` translates to `CircleOps.circumference(circle)`, provi
8184
### Operators
8285

8386
The extension method syntax also applies to the definition of operators.
84-
In this case it is allowed and preferable to omit the period between the leading parameter list
85-
and the operator. In each case the definition syntax mirrors the way the operator is applied.
87+
In each case the definition syntax mirrors the way the operator is applied.
8688
Examples:
8789
```scala
8890
def (x: String) < (y: String) = ...
8991
def (x: Elem) +: (xs: Seq[Elem]) = ...
90-
def (x: Number) min (y: Number) = ...
9192

9293
"ab" < "c"
9394
1 +: List(2, 3)
94-
x min 3
9595
```
96-
The three definitions above translate to
96+
The two definitions above translate to
9797
```scala
9898
def < (x: String)(y: String) = ...
9999
def +: (xs: Seq[Elem])(x: Elem) = ...
100-
def min(x: Number)(y: Number) = ...
101100
```
102-
Note the swap of the two parameters `x` and `xs` when translating
101+
Note that swap of the two parameters `x` and `xs` when translating
103102
the right-binding operator `+:` to an extension method. This is analogous
104103
to the implementation of right binding operators as normal methods.
105104

@@ -122,25 +121,24 @@ If an extension method has type parameters, they come immediately after the `def
122121
```scala
123122
List(1, 2, 3).second[Int]
124123
```
125-
### Collective Extensions
124+
### Given Instances for Extension Methods
126125

127-
A collective extension defines one or more concrete methods that have the same type parameters
128-
and prefix parameter. Examples:
126+
`given` extensions are given instances that define extension methods and nothing else. Examples:
129127

130128
```scala
131-
extension stringOps of (xs: Seq[String]) with {
129+
given stringOps: (xs: Seq[String]) extended with {
132130
def longestStrings: Seq[String] = {
133131
val maxLength = xs.map(_.length).max
134132
xs.filter(_.length == maxLength)
135133
}
136134
}
137135

138-
extension listOps of [T](xs: List[T]) with {
136+
given listOps: [T](xs: List[T]) extended with {
139137
def second = xs.tail.head
140138
def third: T = xs.tail.tail.head
141139
}
142140

143-
extension of [T](xs: List[T])(given Ordering[T]) with {
141+
given [T](xs: List[T])(given Ordering[T]) extended with {
144142
def largest(n: Int) = xs.sorted.takeRight(n)
145143
}
146144
```
@@ -149,7 +147,7 @@ If a given extension is anonymous (as in the last clause), its name is synthesiz
149147
The extensions above are equivalent to the following regular given instances where the implemented parent is `AnyRef` and the parameters in the `extension` clause are repeated in each extension method definition:
150148
```scala
151149
given stringOps: AnyRef {
152-
def (xs: Seq[String]).longestStrings: Seq[String] = {
150+
def (xs: Seq[String]) longestStrings: Seq[String] = {
153151
val maxLength = xs.map(_.length).max
154152
xs.filter(_.length == maxLength)
155153
}
@@ -158,24 +156,21 @@ given listOps: AnyRef {
158156
def [T](xs: List[T]) second = xs.tail.head
159157
def [T](xs: List[T]) third: T = xs.tail.tail.head
160158
}
161-
given extension_largest_List_T: AnyRef {
159+
given given_largest_of_List_T: AnyRef {
162160
def [T](xs: List[T]) largest (given Ordering[T])(n: Int) =
163161
xs.sorted.takeRight(n)
164162
}
165163
```
166164

167-
`extension` and `of` are soft keywords. They can also be used as a regular identifiers.
168-
169165
### Syntax
170166

171167
Here are the syntax changes for extension methods and given extensions relative
172168
to the [current syntax](../../internals/syntax.md). `extension` is a soft keyword, recognized only after a `given`. It can be used as an identifier everywhere else.
173169
```
174170
DefSig ::= ...
175-
| ExtParamClause [nl] [‘.’] id DefParamClauses
171+
| ExtParamClause [nl] id DefParamClauses
172+
GivenDef ::= ...
173+
[id ‘:’] ‘extension’ ExtParamClause {GivenParamClause} ExtMethods
176174
ExtParamClause ::= [DefTypeParamClause] ‘(’ DefParam ‘)’
177-
TmplDef ::= ...
178-
| ‘extension’ ExtensionDef
179-
ExtensionDef ::= [id] ‘of’ ExtParamClause {GivenParamClause} ‘with’ ExtMethods
180-
ExtMethods ::= ‘{’ ‘def’ DefDef {semi ‘def’ DefDef} ‘}’
175+
ExtMethods ::= [nl] ‘{’ ‘def’ DefDef {semi ‘def’ DefDef} ‘}’
181176
```

0 commit comments

Comments
 (0)