Skip to content

Commit 2110158

Browse files
committed
Change named extension syntax
Change from ``` given extension foo: { ... } ``` to ``` given foo: extension { ... } ``` The new syntax is more regular and pleasing to the eye. It avoids the unusual `: { ... }` symbol combination.
1 parent fa0d0d2 commit 2110158

File tree

12 files changed

+65
-75
lines changed

12 files changed

+65
-75
lines changed

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

Lines changed: 37 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -3336,52 +3336,46 @@ object Parsers {
33363336
*/
33373337
def instanceDef(newStyle: Boolean, start: Offset, mods: Modifiers, instanceMod: Mod) = atSpan(start, nameStart) {
33383338
var mods1 = addMod(mods, instanceMod)
3339-
3340-
def extensionDef() = {
3341-
in.nextToken()
3342-
val name =
3343-
if isIdent then
3344-
val id = ident()
3345-
accept(COLON)
3346-
id
3347-
else EmptyTermName
3348-
indentRegion(name) {
3349-
// TODO: accept type params here
3350-
val leadingParamss = if in.token == LPAREN then paramClause(prefix = true) :: Nil else Nil
3351-
// TODO: accept given params here
3352-
possibleTemplateStart()
3353-
val templ = templateBodyOpt(makeConstructor(Nil, leadingParamss), Nil, Nil)
3354-
ModuleDef(name, templ)
3355-
}
3356-
}
3357-
3358-
def regularDef() = {
3359-
val name =
3360-
if isIdent && !(newStyle && allowOldGiven && isIdent(nme.as)) then ident()
3361-
else EmptyTermName
3362-
indentRegion(name) {
3363-
val tparams = typeParamClauseOpt(ParamOwner.Def)
3364-
var leadingParamss =
3365-
if (in.token == LPAREN && allowOldExtension)
3366-
try paramClause(prefix = true) :: Nil
3367-
finally {
3368-
possibleTemplateStart()
3369-
if (!in.isNestedStart) syntaxErrorOrIncomplete("`{' expected")
3370-
}
3371-
else Nil
3372-
val parents =
3373-
if (!newStyle && in.token == FOR || isIdent(nme.as)) { // for the moment, accept both `given for` and `given as`
3339+
var isExtension = false
3340+
val name =
3341+
if isIdent(nme.extension) then
3342+
in.nextToken()
3343+
isExtension = true
3344+
EmptyTermName
3345+
else if newStyle && allowOldGiven && isIdent(nme.as) then EmptyTermName
3346+
else if isIdent then ident()
3347+
else EmptyTermName
3348+
indentRegion(name) {
3349+
var tparams: List[TypeDef] = Nil
3350+
var leadingParamss: List[List[ValDef]] = Nil
3351+
def parseParams() =
3352+
tparams = typeParamClauseOpt(ParamOwner.Def)
3353+
if in.token == LPAREN then leadingParamss = paramClause(prefix = true) :: Nil
3354+
if !isExtension then parseParams()
3355+
val parents =
3356+
if isExtension then Nil
3357+
else if allowOldGiven && (!newStyle && in.token == FOR || isIdent(nme.as)) then // for the moment, accept both `given for` and `given as`
3358+
in.nextToken()
3359+
tokenSeparated(COMMA, constrApp)
3360+
else if in.token == COLON then
3361+
in.nextToken()
3362+
if isIdent(nme.extension) then
3363+
if tparams.nonEmpty then
3364+
syntaxError(i"no type parameters allowed before `extension`", tparams.head.span)
3365+
if leadingParamss.nonEmpty then
3366+
syntaxError(i"no parameters allowed before `extension`", leadingParamss.head.head.span)
33743367
in.nextToken()
3375-
tokenSeparated(COMMA, constrApp)
3376-
}
3377-
else Nil
3378-
val vparamss = paramClauses(ofInstance = true)
3379-
if (in.token == EQUALS && parents.length == 1 && parents.head.isType) {
3368+
parseParams()
3369+
Nil
3370+
else tokenSeparated(COMMA, constrApp)
3371+
else Nil
3372+
val vparamss = paramClauses(ofInstance = true)
3373+
val gdef =
3374+
if (in.token == EQUALS && parents.length == 1 && parents.head.isType) then
33803375
in.nextToken()
33813376
mods1 |= Final
33823377
DefDef(name, tparams, vparamss, parents.head, expr())
3383-
}
3384-
else {
3378+
else
33853379
possibleTemplateStart()
33863380
val (tparams1, vparamss1) =
33873381
if (leadingParamss.nonEmpty)
@@ -3393,12 +3387,8 @@ object Parsers {
33933387
val templ = templateBodyOpt(makeConstructor(tparams1, vparamss1), parents, Nil)
33943388
if (tparams.isEmpty && vparamss1.isEmpty || leadingParamss.nonEmpty) ModuleDef(name, templ)
33953389
else TypeDef(name.toTypeName, templ)
3396-
}
3397-
}
3390+
finalizeDef(gdef, mods1, start)
33983391
}
3399-
3400-
val gdef = if isIdent(nme.extension) then extensionDef() else regularDef()
3401-
finalizeDef(gdef, mods1, start)
34023392
}
34033393

34043394
/* -------- TEMPLATES ------------------------------------------- */

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

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -82,13 +82,13 @@ and where `T` is the expected type. The following two rewritings are tried in or
8282
So `circle.circumference` translates to `CircleOps.circumference(circle)`, provided
8383
`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`).
8484

85-
### Given Instances for Extension Methods
85+
### Given Instances Defining Only Extension Methods
8686

87-
Given instances that define extension methods can also be defined without a parent clause. In this case the `given` is followed by
87+
Given instances that define extension methods can also be defined without a parent clause. In this case the `given` is followed by the special identifier
8888
`extension`. E.g.,
8989

9090
```scala
91-
given extension stringOps: {
91+
given stringOps: extension {
9292
def (xs: Seq[String]) longestStrings: Seq[String] = {
9393
val maxLength = xs.map(_.length).max
9494
xs.filter(_.length == maxLength)
@@ -101,23 +101,22 @@ given extension {
101101
```
102102
If given extensions are anonymous (as in the second clause), their name is synthesized from the name of the first defined extension method.
103103

104-
Note: `extension` is a soft keyword, it can be used elsewhere
105-
as a normal identifier.
104+
Note: `extension` is a soft keyword, it can be used elsewhere as a normal identifier.
106105

107106
### Given Extensions with Collective Parameters
108107

109108
If a given extension defines several extension methods one can pull out the left parameter section
110109
as well as any type parameters of these extension methods into the given instance itself.
111110
For instance, here is a given instance with two extension methods.
112111
```scala
113-
given extension listOps: {
112+
given listOps: extension {
114113
def (xs: List[T]) second[T]: T = xs.tail.head
115114
def (xs: List[T]) third[T]: T = xs.tail.tail.head
116115
}
117116
```
118117
The repetition in the parameters can be avoided by moving the parameters in front of the opening brace. The following version is a shorthand for the code above.
119118
```scala
120-
given extension listOps: [T](xs: List[T]) {
119+
given listOps: extension[T](xs: List[T]) {
121120
def second: T = xs.tail.head
122121
def third: T = xs.tail.tail.head
123122
}
@@ -171,11 +170,12 @@ As usual, type parameters of the extension method follow the defined method name
171170

172171
### Syntax
173172

174-
The required syntax extension just adds one clause for extension methods relative
175-
to the [current syntax](../../internals/syntax.md).
173+
Here are the required syntax extensions compared to the
174+
[current syntax](../../internals/syntax.md).
176175
```
176+
DefSig ::= ...
177+
| ‘(’ DefParam ‘)’ [nl] id [DefTypeParamClause] DefParamClauses
177178
GivenDef ::= ...
178-
| ‘extension’ [id ‘:’] ExtParamClause TemplateBody
179+
| [id ‘:’] ‘extension’ ExtParamClause TemplateBody
179180
ExtParamClause ::= [DefTypeParamClause] ‘(’ DefParam ‘)’ {GivenParamClause}
180-
| ‘(’ DefParam ‘)’ TemplateBody
181181
```

docs/docs/reference/other-new-features/opaques.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ object Logarithms {
2020
}
2121

2222
// Extension methods define opaque types' public APIs
23-
given extension logarithmOps: {
23+
given logarithmOps: extension {
2424
def (x: Logarithm) toDouble: Double = math.exp(x)
2525
def (x: Logarithm) + (y: Logarithm): Logarithm = Logarithm(math.exp(x) + math.exp(y))
2626
def (x: Logarithm) * (y: Logarithm): Logarithm = Logarithm(x + y)

library/src-bootstrapped/scala/IArray.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ object opaques {
88
opaque type IArray[+T] = Array[_ <: T]
99

1010
/** Defines extension methods for immutable arrays */
11-
given extension arrayOps: {
11+
given arrayOps: extension {
1212

1313
/** The selection operation on an immutable array.
1414
*

tests/neg/extmethod-overload.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
object Test {
2-
given extension A: {
2+
given A: extension {
33
def (x: Int) |+| (y: Int) = x + y
44
}
5-
given extension B: {
5+
given B: extension {
66
def (x: Int) |+| (y: String) = x + y.length
77
}
88
assert((1 |+| 2) == 3) // error ambiguous

tests/neg/i5455.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ object Library {
1111
def toInt(n: Nat): Int = n
1212

1313
}
14-
given NatOps {
14+
given extension {
1515
def (x: Nat) * (y: Nat): Nat = x * y
1616
def (x: Nat) toInt: Int = x
1717
}

tests/pos/i7084.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ object Test {
22

33
type Foo
44

5-
given A {
5+
given extension {
66
def (y: Any) g given Foo: Any = ???
77
}
88

tests/pos/reference/delegates.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ object Instances extends Common {
4848
}
4949
}
5050

51-
given extension stringOps: {
51+
given stringOps: extension {
5252
def (xs: Seq[String]) longestStrings: Seq[String] = {
5353
val maxLength = xs.map(_.length).max
5454
xs.filter(_.length == maxLength)
@@ -152,7 +152,7 @@ object Instances extends Common {
152152
object PostConditions {
153153
opaque type WrappedResult[T] = T
154154

155-
private given extension WrappedResult: {
155+
private given WrappedResult: extension {
156156
def apply[T](x: T): WrappedResult[T] = x
157157
def (x: WrappedResult[T]) unwrap[T]: T = x
158158
}

tests/pos/reference/opaque.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ object Logarithms {
1212
}
1313

1414
// Extension methods define opaque types' public APIs
15-
given LogarithmOps {
15+
given extension {
1616
def (x: Logarithm) toDouble: Double = math.exp(x)
1717
def (x: Logarithm) + (y: Logarithm): Logarithm = Logarithm(math.exp(x) + math.exp(y))
1818
def (x: Logarithm) * (y: Logarithm): Logarithm = Logarithm(x + y)

tests/run/extmethod-overload.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ object Test extends App {
2222
// Test with extension methods in given object
2323
object test1 {
2424

25-
given Foo {
25+
given Foo: extension {
2626
def (x: Int) |+| (y: Int) = x + y
2727
def (x: Int) |+| (y: String) = x + y.length
2828

@@ -97,7 +97,7 @@ object Test extends App {
9797
def (x: Int) yy (y: Int) = x + y
9898
}
9999

100-
given {
100+
given extension {
101101
def (x: Int) yy (y: Int) = x - y
102102
}
103103

tests/run/i6902.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
object Test {
22
given [A] { def (a: A) <<< : A = a }
3-
given { def (b: Int) <<<< : Int = b }
3+
given extension { def (b: Int) <<<< : Int = b }
44

55
def main(args: Array[String]): Unit = {
66
1.<<<

tests/run/instances.scala

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@ object Test extends App {
88

99
case class Circle(x: Double, y: Double, radius: Double)
1010

11-
given extension circleOps: {
11+
given circleOps: extension {
1212
def (c: Circle) circumference: Double = c.radius * math.Pi * 2
1313
}
1414

1515
val circle = new Circle(1, 1, 2.0)
1616

1717
assert(circle.circumference == circleOps.circumference(circle))
1818

19-
given extension stringOps: {
19+
given stringOps: extension {
2020
def (xs: Seq[String]) longestStrings: Seq[String] = {
2121
val maxLength = xs.map(_.length).max
2222
xs.filter(_.length == maxLength)
@@ -25,18 +25,18 @@ object Test extends App {
2525
val names = List("hi", "hello", "world")
2626
assert(names.longestStrings == List("hello", "world"))
2727

28-
given extension seqOps: {
28+
given seqOps: extension {
2929
def (xs: Seq[T]) second[T] = xs.tail.head
3030
}
3131

3232
assert(names.longestStrings.second == "world")
3333

34-
given extension listListOps: {
34+
given listListOps: extension {
3535
def (xs: List[List[T]]) flattened[T] = xs.foldLeft[List[T]](Nil)(_ ++ _)
3636
}
3737

3838
// A right associative op
39-
given extension prepend: {
39+
given prepend: extension {
4040
def (x: T) ::[T] (xs: Seq[T]) = x +: xs
4141
}
4242
val ss: Seq[Int] = List(1, 2, 3)

0 commit comments

Comments
 (0)