Skip to content

Commit 318e9f6

Browse files
committed
Docs for extension instances
1 parent 26de50a commit 318e9f6

File tree

1 file changed

+91
-23
lines changed

1 file changed

+91
-23
lines changed

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

Lines changed: 91 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -124,59 +124,127 @@ If an extension method has type parameters, they come immediately after the `def
124124
```scala
125125
List(1, 2, 3).second[Int]
126126
```
127-
### Collective Extensions
128127

129-
A collective extension defines one or more concrete methods that have the same type parameters
130-
and prefix parameter. Examples:
128+
### Extension Instances
131129

130+
It is quite common to wrap one or more extension methods in a given instance,
131+
in order to make them available as methods without needing to be imported explicitly.
132+
This pattern is supported by a special `extension` syntax. Example:
132133
```scala
133-
extension stringOps on (xs: Seq[String]) {
134-
def longestStrings: Seq[String] = {
134+
extension ops {
135+
def (xs: Seq[String]).longestStrings: Seq[String] = {
135136
val maxLength = xs.map(_.length).max
136137
xs.filter(_.length == maxLength)
137138
}
139+
def (xs: Seq[String]).longestString: String = xs.longestStrings.head
140+
def [T](xs: List[T]).second: T = xs.tail.head
141+
}
142+
```
143+
An extension instance can only contain extension methods. Other definitions are not allowed. The name `ops`
144+
of the extension is optional. It can be left out:
145+
```scala
146+
extension {
147+
def (xs: Seq[String]).longestStrings: Seq[String] = ...
148+
def [T](xs: List[T]).second: T = ...
149+
}
150+
```
151+
If the name of an extension is not explicitly given, it is synthesized from the name and type of the first implemented extension method.
152+
153+
Extension instances map directly to given instances. The `ops` extension above
154+
would expand to
155+
```scala
156+
given ops as AnyRef {
157+
def (xs: Seq[String]).longestStrings: Seq[String] = ...
158+
def (xs: Seq[String]).longestString: String = ...
159+
def [T](xs: List[T]).second: T = ...
160+
}
161+
```
162+
The type "implemented" by this given instance is `AnyRef`, which
163+
is not a type one can summon by itself. This means that the instance can
164+
only be used for its extension methods.
165+
166+
### Collective Extensions
167+
168+
Sometimes, one wants to define several extension methods that share the same
169+
left-hand parameter type. In this case one can "pull out" the common parameters
170+
into the extension instance itself. Examples:
171+
```scala
172+
extension stringOps on (ss: Seq[String]) {
173+
def longestStrings: Seq[String] = {
174+
val maxLength = ss.map(_.length).max
175+
ss.filter(_.length == maxLength)
176+
}
177+
def longestString: String = longestStrings.head
138178
}
139179

140180
extension listOps on [T](xs: List[T]) {
141-
def second = xs.tail.head
142-
def third: T = xs.tail.tail.head
181+
def second: T = xs.tail.head
182+
def third: T = xs.tail.second
143183
}
144184

145185
extension on [T](xs: List[T])(using Ordering[T]) {
146186
def largest(n: Int) = xs.sorted.takeRight(n)
147187
}
148188
```
149-
If an extension is anonymous (as in the last clause), its name is synthesized from the name of the first defined extension method.
150-
151-
The extensions above are equivalent to the following regular given instances where the implemented parent is `AnyRef` and the leading parameters are repeated in each extension method definition:
189+
Collective extensions like these are a shorthand for extension instances where
190+
the parameters following the `on` are repeated for each implemented method.
191+
Furthermore, each method's body starts with a synthesized import that
192+
imports all other names of methods defined in the same extension. This lets
193+
one use co-defined extension methods without the repeated prefix parameter,
194+
as is shown in the body of the `longestString` method above.
195+
196+
For instance, the collective extensions above are equivalent to the following extension instances:
152197
```scala
153-
given stringOps as AnyRef {
154-
def (xs: Seq[String]).longestStrings: Seq[String] = {
155-
val maxLength = xs.map(_.length).max
156-
xs.filter(_.length == maxLength)
198+
extension stringOps {
199+
def (ss: Seq[String]).longestStrings: Seq[String] = {
200+
import ss.{longestStrings, longestString}
201+
val maxLength = ss.map(_.length).max
202+
ss.filter(_.length == maxLength)
203+
}
204+
def (ss: Seq[String]).longestString: String = {
205+
import ss.{longestStrings, longestString}
206+
longestStrings.head
157207
}
158208
}
159-
given listOps as AnyRef {
160-
def [T](xs: List[T]).second = xs.tail.head
161-
def [T](xs: List[T]).third: T = xs.tail.tail.head
209+
extension listOps {
210+
def [T](xs: List[T]).second: T = {
211+
import xs.{second, third}
212+
xs.tail.head
213+
}
214+
def [T](xs: List[T]).third: T = {
215+
import xs.{second, third}
216+
xs.tail.second
217+
}
162218
}
163-
given extension_largest_List_T as AnyRef {
164-
def [T](xs: List[T]).largest(using Ordering[T])(n: Int) =
219+
extension {
220+
def [T](xs: List[T]).largest(using Ordering[T])(n: Int) = {
221+
import xs.largest
165222
xs.sorted.takeRight(n)
223+
}
166224
}
167225
```
168226

169227
### Syntax
170228

171229
Here are the syntax changes for extension methods and collective extensions relative
172-
to the [current syntax](../../internals/syntax.md). `extension` is a soft keyword, recognized only in tandem with `on`. It can be used as an identifier everywhere else.
173-
230+
to the [current syntax](../../internals/syntax.md).
174231
```
175232
DefSig ::= ...
176233
| ExtParamClause [nl] [‘.’] id DefParamClauses
177234
ExtParamClause ::= [DefTypeParamClause] ‘(’ DefParam ‘)’
178235
TmplDef ::= ...
179236
| ‘extension’ ExtensionDef
180-
ExtensionDef ::= [id] ‘on’ ExtParamClause {GivenParamClause} ExtMethods
181-
ExtMethods ::= ‘{’ ‘def’ DefDef {semi ‘def’ DefDef} ‘}’
237+
ExtensionDef ::= [id] [‘on’ ExtParamClause {GivenParamClause}] TemplateBody
182238
```
239+
The template body of an extension must consist only of extension method definitions for a regular
240+
extension instance, and only of normal method definitions for a collective extension instance.
241+
It must not be empty.
242+
243+
`extension` and `on` are soft keywords, recognized only when they appear at the start of a
244+
statement in one of the patterns
245+
```scala
246+
extension on ...
247+
extension <ident> on ...
248+
extension { ...
249+
extension <ident> { ...
250+
```

0 commit comments

Comments
 (0)