Skip to content

Commit 67d2354

Browse files
committed
Change syntax from extend to extension
1 parent d708554 commit 67d2354

File tree

5 files changed

+51
-29
lines changed

5 files changed

+51
-29
lines changed

compiler/src/dotty/tools/dotc/parsing/Parsers.scala

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3398,28 +3398,32 @@ object Parsers {
33983398

33993399
/** GivenDef ::= [GivenSig (‘:’ | <:)] Type ‘=’ Expr
34003400
* | [GivenSig ‘:’] [ConstrApp {‘,’ ConstrApp }] [TemplateBody]
3401-
* | [[id] ‘extends’ ExtParamClause {GivenParamClause} ExtMethods
3401+
* | [id ‘:’] ‘extension’ ExtParamClause {GivenParamClause} ExtMethods
34023402
* GivenSig ::= [id] [DefTypeParamClause] {GivenParamClause}
34033403
* ExtParamClause ::= [DefTypeParamClause] DefParamClause {GivenParamClause}
34043404
* ExtMethods ::= [nl] ‘{’ ‘def’ DefDef {semi ‘def’ DefDef} ‘}’
34053405
*/
34063406
def givenDef(start: Offset, mods: Modifiers, instanceMod: Mod) = atSpan(start, nameStart) {
34073407
var mods1 = addMod(mods, instanceMod)
34083408
val hasGivenSig = followingIsGivenSig()
3409-
val name =
3410-
if isIdent && (hasGivenSig || in.lookaheadIn(BitSet(EXTENDS))) then ident()
3411-
else EmptyTermName
3409+
val (name, isExtension) =
3410+
if isIdent && hasGivenSig then
3411+
(ident(), in.token == COLON && in.lookaheadIn(nme.extension))
3412+
else
3413+
(EmptyTermName, isIdent(nme.extension))
3414+
34123415
val gdef = indentRegion(name) {
3413-
if in.token == EXTENDS then
3414-
in.nextToken()
3416+
if isExtension then
3417+
if (in.token == COLON) in.nextToken()
3418+
assert(ident() == nme.extension)
34153419
val tparams = typeParamClauseOpt(ParamOwner.Def)
34163420
val extParams = paramClause(0, prefix = true)
34173421
val givenParamss = paramClauses(givenOnly = true)
34183422
possibleTemplateStart()
3419-
val extMethods = templateBodyOpt(
3423+
val templ = templateBodyOpt(
34203424
makeConstructor(tparams, extParams :: givenParamss), Nil, Nil)
3421-
extMethods.body.foreach(checkExtensionMethod)
3422-
ModuleDef(name, extMethods)
3425+
templ.body.foreach(checkExtensionMethod)
3426+
ModuleDef(name, templ)
34233427
else
34243428
var tparams: List[TypeDef] = Nil
34253429
var vparamss: List[List[ValDef]] = Nil

compiler/src/dotty/tools/dotc/parsing/Scanners.scala

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -895,14 +895,15 @@ object Scanners {
895895
nextToken()
896896

897897
/** Is the token following the current one in `tokens`? */
898-
def lookaheadIn(tokens: BitSet): Boolean = {
898+
def lookaheadIn(follow: BitSet | TermName): Boolean =
899899
val lookahead = LookaheadScanner()
900900
while
901901
lookahead.nextToken()
902902
lookahead.isNewLine
903903
do ()
904-
tokens.contains(lookahead.token)
905-
}
904+
follow match
905+
case tokens: BitSet => tokens.contains(lookahead.token)
906+
case name: TermName => lookahead.token == IDENTIFIER && lookahead.name == name
906907

907908
/** Is the current token in a position where a modifier is allowed? */
908909
def inModifierPosition(): Boolean = {

docs/docs/internals/syntax.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -388,9 +388,10 @@ ObjectDef ::= id [Template]
388388
EnumDef ::= id ClassConstr InheritClauses EnumBody EnumDef(mods, name, tparams, template)
389389
GivenDef ::= [GivenSig (‘:’ | <:)] Type ‘=’ Expr
390390
| [GivenSig ‘:’] [ConstrApp {‘,’ ConstrApp }] [TemplateBody]
391-
| [GivenSig ‘:’] ExtParamClause ExtMethods
391+
| [[id ‘:’] ‘extension’ ExtParamClause {GivenParamClause}
392+
ExtMethods
392393
GivenSig ::= [id] [DefTypeParamClause] {GivenParamClause}
393-
ExtParamClause ::= [DefTypeParamClause] ‘(’ DefParam ‘)’ {GivenParamClause}
394+
ExtParamClause ::= [DefTypeParamClause] ‘(’ DefParam ‘)’
394395
ExtMethods ::= [nl] ‘{’ ‘def’ DefDef {semi ‘def’ DefDef} ‘}’
395396
Template ::= InheritClauses [TemplateBody] Template(constr, parents, self, stats)
396397
InheritClauses ::= [‘extends’ ConstrApps] [‘derives’ QualId {‘,’ QualId}]

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

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -123,48 +123,54 @@ List(1, 2, 3).second[Int]
123123
```
124124
### Given Instances for Extension Methods
125125

126-
The `given extends` syntax lets on define given instances that define extension methods and nothing else. Examples:
126+
`given` extensions are given instances that define extension methods and nothing else. Examples:
127127

128128
```scala
129-
given stringOps extends (xs: Seq[String]) {
129+
given stringOps: extension (xs: Seq[String]) {
130130
def longestStrings: Seq[String] = {
131131
val maxLength = xs.map(_.length).max
132132
xs.filter(_.length == maxLength)
133133
}
134134
}
135135

136-
given extends [T](xs: List[T]) {
136+
given listOps: extension [T](xs: List[T]) {
137137
def second = xs.tail.head
138138
def third: T = xs.tail.tail.head
139139
}
140-
```
141-
If such given instances are anonymous (as in the second clause), their name is synthesized from the name of the first defined extension method.
142140

143-
The extension method definitions above are equivalent to the following standard given instances where
144-
the implemented parent is `AnyRef` and the parameters after the `extend` clause are repeated in each
145-
method definition:
141+
given extension [T](xs: List[T])(given Ordering[T]) {
142+
def largest(n: Int) = xs.sort.takeRight(n)
143+
}
146144
```
145+
If a given extension is anonymous (as in the last clause), its name is synthesized from the name of the first defined extension method.
146+
147+
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:
148+
```scala
147149
given stringOps: AnyRef {
148150
def (xs: Seq[String]) longestStrings: Seq[String] = {
149151
val maxLength = xs.map(_.length).max
150152
xs.filter(_.length == maxLength)
151153
}
152154
}
153-
given AnyRef {
155+
given listOps: AnyRef {
154156
def [T](xs: List[T]) second = xs.tail.head
155157
def [T](xs: List[T]) third: T = xs.tail.tail.head
156158
}
159+
given AnyRef {
160+
def [T](xs: List[T]) largest (given Ordering[T])(n: Int) =
161+
xs.sort.takeRight(n)
162+
}
157163
```
158164

159165
### Syntax
160166

161-
The required syntax extension just adds one clause for extension methods relative
162-
to the [current syntax](../../internals/syntax.md).
167+
Here are the syntax changes for extension methods and given extensions relative
168+
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.
163169
```
164170
DefSig ::= ...
165171
| ExtParamClause [nl] id DefParamClauses
166172
GivenDef ::= ...
167-
[id] ‘extends’ ExtParamClause {GivenParamClause} ExtMethods
173+
[id ‘:’] ‘extension’ ExtParamClause {GivenParamClause} ExtMethods
168174
ExtParamClause ::= [DefTypeParamClause] ‘(’ DefParam ‘)’
169175
ExtMethods ::= [nl] ‘{’ ‘def’ DefDef {semi ‘def’ DefDef} ‘}’
170176
```

tests/pos/reference/extension-methods.scala

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,26 +41,36 @@ object Test with
4141

4242
List(1, 2, 3).second[Int]
4343

44-
given stringOps extends (xs: Seq[String]) {
44+
given stringOps: extension (xs: Seq[String]) {
4545
def longestStrings: Seq[String] = {
4646
val maxLength = xs.map(_.length).max
4747
xs.filter(_.length == maxLength)
4848
}
4949
}
5050

51-
given extends [T](xs: List[T]) with
51+
given listOps: extension [T](xs: List[T]) with
5252
def second = xs.tail.head
5353
def third: T = xs.tail.tail.head
5454

55+
56+
given extension [T](xs: List[T])(given Ordering[T]) with
57+
def largest(n: Int) = xs.sorted.takeRight(n)
58+
5559
given stringOps1: AnyRef {
5660
def (xs: Seq[String]) longestStrings: Seq[String] = {
5761
val maxLength = xs.map(_.length).max
5862
xs.filter(_.length == maxLength)
5963
}
6064
}
61-
given AnyRef {
65+
66+
given listOps1: AnyRef {
6267
def [T](xs: List[T]) second = xs.tail.head
6368
def [T](xs: List[T]) third: T = xs.tail.tail.head
6469
}
6570

71+
given AnyRef {
72+
def [T](xs: List[T]) largest (given Ordering[T])(n: Int) =
73+
xs.sorted.takeRight(n)
74+
}
75+
6676
end Test

0 commit comments

Comments
 (0)