Skip to content

Commit 290ab7b

Browse files
committed
Document new changes
The new syntax allows arbitrary interleaving of type and term clauses for methods TypelessClause{|s} is added for class constructors since they still don't allow interleaving of type parameters Also unifies the affected grammar variable names so that <x>s means "one or more <x>" Implicit clauses are moved apart from term clause to make it more obvious they can only be the last clause
1 parent be120fa commit 290ab7b

File tree

4 files changed

+151
-30
lines changed

4 files changed

+151
-30
lines changed

docs/_docs/internals/syntax.md

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -347,9 +347,6 @@ ClsTypeParamClause::= ‘[’ ClsTypeParam {‘,’ ClsTypeParam} ‘]’
347347
ClsTypeParam ::= {Annotation} [‘+’ | ‘-’] TypeDef(Modifiers, name, tparams, bounds)
348348
id [HkTypeParamClause] TypeParamBounds Bound(below, above, context)
349349
350-
DefTypeParamClause::= ‘[’ DefTypeParam {‘,’ DefTypeParam} ‘]’
351-
DefTypeParam ::= {Annotation} id [HkTypeParamClause] TypeParamBounds
352-
353350
TypTypeParamClause::= ‘[’ TypTypeParam {‘,’ TypTypeParam} ‘]’
354351
TypTypeParam ::= {Annotation} id [HkTypeParamClause] TypeBounds
355352
@@ -363,13 +360,24 @@ ClsParamClause ::= [nl] ‘(’ ClsParams ‘)’
363360
ClsParams ::= ClsParam {‘,’ ClsParam}
364361
ClsParam ::= {Annotation} ValDef(mods, id, tpe, expr) -- point of mods on val/var
365362
[{Modifier} (‘val’ | ‘var’) | ‘inline’] Param
366-
Param ::= id ‘:’ ParamType [‘=’ Expr]
367363
368-
DefParamClauses ::= {DefParamClause} [[nl] ‘(’ [‘implicit’] DefParams ‘)’]
369-
DefParamClause ::= [nl] ‘(’ DefParams ‘)’ | UsingParamClause
370-
UsingParamClause ::= [nl] ‘(’ ‘using’ (DefParams | FunArgTypes) ‘)’
371-
DefParams ::= DefParam {‘,’ DefParam}
372-
DefParam ::= {Annotation} [‘inline’] Param ValDef(mods, id, tpe, expr) -- point of mods at id.
364+
DefParamClauses ::= DefParamClause { DefParamClause }
365+
DefParamClause ::= DefTypeParamClause
366+
| DefTermParamClause
367+
| UsingParamClause
368+
TypelessClauses ::= TypelessClause {TypelessClause}
369+
TypelessClause ::= DefTermParamClause
370+
| UsingParamClause
371+
372+
DefTypeParamClause::= [nl] ‘[’ DefTypeParam {‘,’ DefTypeParam} ‘]’
373+
DefTypeParam ::= {Annotation} id [HkTypeParamClause] TypeParamBounds
374+
DefTermParamClause::= [nl] ‘(’ [DefTermParams] ‘)’
375+
UsingParamClause ::= [nl] ‘(’ ‘using’ (DefTermParams | FunArgTypes) ‘)’
376+
DefImplicitClause ::= [nl] ‘(’ ‘implicit’ DefTermParams ‘)’
377+
378+
DefTermParams ::= DefTermParam {‘,’ DefTermParam}
379+
DefTermParam ::= {Annotation} [‘inline’] Param ValDef(mods, id, tpe, expr) -- point of mods at id.
380+
Param ::= id ‘:’ ParamType [‘=’ Expr]
373381
```
374382

375383
### Bindings and Imports
@@ -420,7 +428,7 @@ Dcl ::= RefineDcl
420428
ValDcl ::= ids ‘:’ Type PatDef(_, ids, tpe, EmptyTree)
421429
VarDcl ::= ids ‘:’ Type PatDef(_, ids, tpe, EmptyTree)
422430
DefDcl ::= DefSig ‘:’ Type DefDef(_, name, tparams, vparamss, tpe, EmptyTree)
423-
DefSig ::= id [DefTypeParamClause] DefParamClauses
431+
DefSig ::= id [DefParamClauses] [DefImplicitClause]
424432
TypeDcl ::= id [TypeParamClause] {FunParamClause} TypeBounds TypeDefTree(_, name, tparams, bound
425433
[‘=’ Type]
426434
@@ -431,8 +439,8 @@ Def ::= ‘val’ PatDef
431439
| TmplDef
432440
PatDef ::= ids [‘:’ Type] ‘=’ Expr
433441
| Pattern2 [‘:’ Type] ‘=’ Expr PatDef(_, pats, tpe?, expr)
434-
DefDef ::= DefSig [‘:’ Type] ‘=’ Expr DefDef(_, name, tparams, vparamss, tpe, expr)
435-
| ‘this’ DefParamClause DefParamClauses ‘=’ ConstrExpr DefDef(_, <init>, Nil, vparamss, EmptyTree, expr | Block)
442+
DefDef ::= DefSig [‘:’ Type] ‘=’ Expr DefDef(_, name, paramss, tpe, expr)
443+
| ‘this’ TypelessClauses [DefImplicitClause] ‘=’ ConstrExpr DefDef(_, <init>, vparamss, EmptyTree, expr | Block)
436444
437445
TmplDef ::= ([‘case’] ‘class’ | ‘trait’) ClassDef
438446
| [‘case’] ‘object’ ObjectDef
@@ -444,10 +452,10 @@ ConstrMods ::= {Annotation} [AccessModifier]
444452
ObjectDef ::= id [Template] ModuleDef(mods, name, template) // no constructor
445453
EnumDef ::= id ClassConstr InheritClauses EnumBody
446454
GivenDef ::= [GivenSig] (AnnotType [‘=’ Expr] | StructuralInstance)
447-
GivenSig ::= [id] [DefTypeParamClause] {UsingParamClause} ‘:’ -- one of `id`, `DefParamClause`, `UsingParamClause` must be present
455+
GivenSig ::= [id] [DefTypeParamClause] {UsingParamClause} ‘:’ -- one of `id`, `DefTypeParamClause`, `UsingParamClause` must be present
448456
StructuralInstance ::= ConstrApp {‘with’ ConstrApp} [‘with’ WithTemplateBody]
449457
Extension ::= ‘extension’ [DefTypeParamClause] {UsingParamClause}
450-
‘(’ DefParam ‘)’ {UsingParamClause} ExtMethods
458+
‘(’ DefTermParam ‘)’ {UsingParamClause} ExtMethods
451459
ExtMethods ::= ExtMethod | [nl] <<< ExtMethod {semi ExtMethod} >>>
452460
ExtMethod ::= {Annotation [nl]} {Modifier} ‘def’ DefDef
453461
| Export

docs/_docs/reference/contextual/using-clauses.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,8 +153,8 @@ Here is the new syntax of parameters and arguments seen as a delta from the [sta
153153

154154
```
155155
ClsParamClause ::= ... | UsingClsParamClause
156-
DefParamClauses ::= ... | UsingParamClause
156+
DefParamClause ::= ... | UsingParamClause
157157
UsingClsParamClause ::= ‘(’ ‘using’ (ClsParams | Types) ‘)’
158-
UsingParamClause ::= ‘(’ ‘using’ (DefParams | Types) ‘)’
158+
UsingParamClause ::= ‘(’ ‘using’ (DefTermParams | Types) ‘)’
159159
ParArgumentExprs ::= ... | ‘(’ ‘using’ ExprsInParens ‘)’
160160
```
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
---
2+
layout: doc-page
3+
title: "Generalized Method Syntax"
4+
movedTo: https://docs.scala-lang.org/scala3/reference/other-new-features/generalized-method-syntax.html
5+
---
6+
7+
The inclusion of using clauses is not the only way in which methods have been updated, type parameter clauses are now allowed in any number and at any position.
8+
9+
## Syntax Changes
10+
11+
### In Scala 2
12+
13+
The old syntax only allowed zero or one type parameter clause, followed by any number of term clauses, optionnally followed by an implicit clause:
14+
15+
```scala
16+
def foo[T, U](x: T)(y: U)(z: Int, s: String)(a: Array[T])(implicit ordInt: Ord[Int], l: List[U])
17+
```
18+
19+
### In Scala 3
20+
21+
The new syntax allows any number of type, term and using clause, in any order, optionnaly followed by an implicit clause:
22+
(do note however that [implicit clause are discouraged, in favor of using clauses](https://docs.scala-lang.org/scala3/reference/contextual/relationship-implicits.html))
23+
24+
```scala
25+
def foo[T, U](x: T)(y: U)[V](z: V, s: String)[A](a: Array[A])(implicit List[U])
26+
```
27+
28+
### Unchanged
29+
30+
Class definitions and type declarations are unaffected, there can only be up to one type clause, in leading posion.
31+
32+
## Motivation
33+
34+
The new syntax is a powerful but natural extension of the old one, it allows new design patterns while staying intuitive and legible.
35+
36+
### Dependent Type Clauses
37+
38+
As type clauses can come after term clauses, it is now possible to have type parameters that depend on term parameters:
39+
40+
```scala
41+
trait Key { type Value }
42+
trait DB {
43+
def get(k: Key): Option[k.Value] // dependent result type
44+
def getOrElse(k: Key)[V >: k.Value](default: V): V // dependent type parameter
45+
}
46+
```
47+
48+
Note that simply replacing `V` by `k.Value` would not be equivalent. For example, if `k.Value` is `Some[Int]`, only the above allows:
49+
`getOrElse(k)[Option[Int]](None)`, which returns a `Number`.
50+
51+
### Partial Inference
52+
53+
It is now possible to only infer some of the type parameters, this reduces boilerplate at the use site:
54+
```scala
55+
trait StaticSizeList[S <: Int & Singleton, T]
56+
def filled[S <: Int & Singleton][T](x: T): StaticSizeList[S,T] = ???
57+
val helloes = filled[4]("Hello!") // S=4, and T is inferred
58+
```
59+
60+
## Details
61+
62+
### Application
63+
64+
Method application is unchanged.
65+
When multiple type clauses are expected but not all are passed, the rightmost ones are inferred.
66+
67+
In particular, the following does not type check, even though the argument `Char` is only valid for `C`:
68+
```scala
69+
def triple[I <: Int](using Ordering[I])[C <: Char](a: I, b: C) = ???
70+
triple[Char](0, 'c') // error: Char does not conform to upperbound Int
71+
```
72+
73+
### Extension Methods
74+
75+
Extension methods follow the same syntax, for example the following is valid:
76+
```scala
77+
extension [T](l1: List[T])
78+
def zipWith[U](l2: List[U])[V](l3: List[V]): List[(T,U,V)]
79+
```
80+
81+
### When to use
82+
83+
We recommand to always put a unique type clause at the beginning, unless it is not possible to do so.
84+
For example, the extension method `zipWith` above should be written `zipWith[U, V](l2: List[U], l3: List[V]): List[(T,U,V)]` instead.
85+
On the other hand, the `getOrElse` method is recommended as-is, as it cannot be written with a leading type clause.
86+
87+
### Formal syntax
88+
89+
```
90+
DefDcl ::= DefSig ‘:’ Type
91+
DefDef ::= DefSig [‘:’ Type] ‘=’ Expr
92+
DefSig ::= id [DefParamClauses] [DefImplicitClause]
93+
DefParamClauses ::= DefParamClause { DefParamClause }
94+
DefParamClause ::= DefTypeParamClause
95+
| DefTermParamClause
96+
| UsingParamClause
97+
DefTypeParamClause::= [nl] ‘[’ DefTypeParam {‘,’ DefTypeParam} ‘]’
98+
DefTypeParam ::= {Annotation} id [HkTypeParamClause] TypeParamBounds
99+
DefTermParamClause::= [nl] ‘(’ [DefTermParams] ‘)’
100+
UsingParamClause ::= [nl] ‘(’ ‘using’ (DefTermParams | FunArgTypes) ‘)’
101+
DefImplicitClause ::= [nl] ‘(’ ‘implicit’ DefTermParams ‘)’
102+
DefTermParams ::= DefTermParam {‘,’ DefTermParam}
103+
DefTermParam ::= {Annotation} [‘inline’] Param
104+
Param ::= id ‘:’ ParamType [‘=’ Expr]
105+
```

docs/_docs/reference/syntax.md

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -338,9 +338,6 @@ ArgumentPatterns ::= ‘(’ [Patterns] ‘)’
338338
ClsTypeParamClause::= ‘[’ ClsTypeParam {‘,’ ClsTypeParam} ‘]’
339339
ClsTypeParam ::= {Annotation} [‘+’ | ‘-’] id [HkTypeParamClause] TypeParamBounds
340340
341-
DefTypeParamClause::= ‘[’ DefTypeParam {‘,’ DefTypeParam} ‘]’
342-
DefTypeParam ::= {Annotation} id [HkTypeParamClause] TypeParamBounds
343-
344341
TypTypeParamClause::= ‘[’ TypTypeParam {‘,’ TypTypeParam} ‘]’
345342
TypTypeParam ::= {Annotation} id [HkTypeParamClause] TypeBounds
346343
@@ -352,13 +349,24 @@ ClsParamClause ::= [nl] ‘(’ ClsParams ‘)’
352349
| [nl] ‘(’ ‘using’ (ClsParams | FunArgTypes) ‘)’
353350
ClsParams ::= ClsParam {‘,’ ClsParam}
354351
ClsParam ::= {Annotation} [{Modifier} (‘val’ | ‘var’) | ‘inline’] Param
355-
Param ::= id ‘:’ ParamType [‘=’ Expr]
356352
357-
DefParamClauses ::= {DefParamClause} [[nl] ‘(’ [‘implicit’] DefParams ‘)’]
358-
DefParamClause ::= [nl] ‘(’ DefParams ‘)’ | UsingParamClause
359-
UsingParamClause ::= [nl] ‘(’ ‘using’ (DefParams | FunArgTypes) ‘)’
360-
DefParams ::= DefParam {‘,’ DefParam}
361-
DefParam ::= {Annotation} [‘inline’] Param
353+
DefParamClauses ::= DefParamClause { DefParamClause }
354+
DefParamClause ::= DefTypeParamClause
355+
| DefTermParamClause
356+
| UsingParamClause
357+
TypelessClauses ::= TypelessClause {TypelessClause}
358+
TypelessClause ::= DefTermParamClause
359+
| UsingParamClause
360+
361+
DefTypeParamClause::= [nl] ‘[’ DefTypeParam {‘,’ DefTypeParam} ‘]’
362+
DefTypeParam ::= {Annotation} id [HkTypeParamClause] TypeParamBounds
363+
DefTermParamClause::= [nl] ‘(’ [DefTermParams] ‘)’
364+
UsingParamClause ::= [nl] ‘(’ ‘using’ (DefTermParams | FunArgTypes) ‘)’
365+
DefImplicitClause ::= [nl] ‘(’ ‘implicit’ DefTermParams ‘)’
366+
367+
DefTermParams ::= DefTermParam {‘,’ DefTermParam}
368+
DefTermParam ::= {Annotation} [‘inline’] Param
369+
Param ::= id ‘:’ ParamType [‘=’ Expr]
362370
```
363371

364372
### Bindings and Imports
@@ -409,8 +417,8 @@ Dcl ::= RefineDcl
409417
ValDcl ::= ids ‘:’ Type
410418
VarDcl ::= ids ‘:’ Type
411419
DefDcl ::= DefSig ‘:’ Type
412-
DefSig ::= id [DefTypeParamClause] DefParamClauses
413-
TypeDcl ::= id [TypeParamClause] {FunParamClause} TypeBounds [‘=’ Type]
420+
DefSig ::= id [DefParamClauses] [DefImplicitClause]
421+
TypeDcl ::= id [TypeParamClause] {FunParamClause} TypeBounds
414422
415423
Def ::= ‘val’ PatDef
416424
| ‘var’ PatDef
@@ -420,7 +428,7 @@ Def ::= ‘val’ PatDef
420428
PatDef ::= ids [‘:’ Type] ‘=’ Expr
421429
| Pattern2 [‘:’ Type] ‘=’ Expr
422430
DefDef ::= DefSig [‘:’ Type] ‘=’ Expr
423-
| ‘this’ DefParamClause DefParamClauses ‘=’ ConstrExpr
431+
| ‘this’ TypelessClauses [DefImplicitClause] ‘=’ ConstrExpr
424432
425433
TmplDef ::= ([‘case’] ‘class’ | ‘trait’) ClassDef
426434
| [‘case’] ‘object’ ObjectDef
@@ -432,10 +440,10 @@ ConstrMods ::= {Annotation} [AccessModifier]
432440
ObjectDef ::= id [Template]
433441
EnumDef ::= id ClassConstr InheritClauses EnumBody
434442
GivenDef ::= [GivenSig] (AnnotType [‘=’ Expr] | StructuralInstance)
435-
GivenSig ::= [id] [DefTypeParamClause] {UsingParamClause} ‘:’ -- one of `id`, `DefParamClause`, `UsingParamClause` must be present
443+
GivenSig ::= [id] [DefTypeParamClause] {UsingParamClause} ‘:’ -- one of `id`, `DefTypeParamClause`, `UsingParamClause` must be present
436444
StructuralInstance ::= ConstrApp {‘with’ ConstrApp} [‘with’ WithTemplateBody]
437445
Extension ::= ‘extension’ [DefTypeParamClause] {UsingParamClause}
438-
‘(’ DefParam ‘)’ {UsingParamClause} ExtMethods
446+
‘(’ DefTermParam ‘)’ {UsingParamClause} ExtMethods
439447
ExtMethods ::= ExtMethod | [nl] <<< ExtMethod {semi ExtMethod} >>>
440448
ExtMethod ::= {Annotation [nl]} {Modifier} ‘def’ DefDef
441449
| Export

0 commit comments

Comments
 (0)