Skip to content

Commit c34cf2b

Browse files
committed
Use given for implicit parameters and arguments
1 parent f37526b commit c34cf2b

33 files changed

+331
-253
lines changed

compiler/src/dotty/tools/dotc/core/Types.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3879,11 +3879,11 @@ object Types {
38793879
def selfType(implicit ctx: Context): Type = {
38803880
if (selfTypeCache == null)
38813881
selfTypeCache = {
3882-
val given = cls.givenSelfType
3883-
if (!given.isValueType) appliedRef
3884-
else if (cls is Module) given
3882+
val givenSelf = cls.givenSelfType
3883+
if (!givenSelf.isValueType) appliedRef
3884+
else if (cls is Module) givenSelf
38853885
else if (ctx.erasedTypes) appliedRef
3886-
else AndType(given, appliedRef)
3886+
else AndType(givenSelf, appliedRef)
38873887
}
38883888
selfTypeCache
38893889
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -535,7 +535,7 @@ object Parsers {
535535
}
536536
else recur(operand())
537537
}
538-
else if (in.token == WITH) {
538+
else if (in.token == GIVEN) {
539539
val top1 = reduceStack(base, top, minInfixPrec, leftAssoc = true, nme.WITHkw, isType)
540540
assert(opStack `eq` base)
541541
val app = atSpan(startOffset(top1), in.offset) {
@@ -2139,7 +2139,7 @@ object Parsers {
21392139
ofInstance: Boolean = false): List[List[ValDef]] = {
21402140
def recur(firstClause: Boolean, nparams: Int): List[List[ValDef]] = {
21412141
val initialMods =
2142-
if (in.token == WITH) {
2142+
if (in.token == GIVEN) {
21432143
in.nextToken()
21442144
Modifiers(Contextual | Implicit)
21452145
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ object Tokens extends TokensCommon {
180180
final val ENUM = 62; enter(ENUM, "enum")
181181
final val ERASED = 63; enter(ERASED, "erased")
182182
final val INSTANCE = 64; enter(INSTANCE, "instance")
183+
final val GIVEN = 65; enter(GIVEN, "given")
183184

184185
/** special symbols */
185186
final val NEWLINE = 78; enter(NEWLINE, "end of statement", "new line")
@@ -201,7 +202,7 @@ object Tokens extends TokensCommon {
201202
/** XML mode */
202203
final val XMLSTART = 96; enter(XMLSTART, "$XMLSTART$<") // TODO: deprecate
203204

204-
final val alphaKeywords: TokenSet = tokenRange(IF, INSTANCE)
205+
final val alphaKeywords: TokenSet = tokenRange(IF, GIVEN)
205206
final val symbolicKeywords: TokenSet = tokenRange(USCORE, VIEWBOUND)
206207
final val symbolicTokens: TokenSet = tokenRange(COMMA, VIEWBOUND)
207208
final val keywords: TokenSet = alphaKeywords | symbolicKeywords

compiler/src/dotty/tools/dotc/typer/Namer.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,9 +118,9 @@ trait NamerContextOps { this: Context =>
118118
/** The given type, unless `sym` is a constructor, in which case the
119119
* type of the constructed instance is returned
120120
*/
121-
def effectiveResultType(sym: Symbol, typeParams: List[Symbol], given: Type): Type =
121+
def effectiveResultType(sym: Symbol, typeParams: List[Symbol], givenTp: Type): Type =
122122
if (sym.name == nme.CONSTRUCTOR) sym.owner.typeRef.appliedTo(typeParams.map(_.typeRef))
123-
else given
123+
else givenTp
124124

125125
/** if isConstructor, make sure it has one non-implicit parameter list */
126126
def normalizeIfConstructor(termParamss: List[List[Symbol]], isConstructor: Boolean): List[List[Symbol]] =

docs/docs/internals/syntax.md

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -92,10 +92,11 @@ semi ::= ‘;’ | nl {nl}
9292

9393
```
9494
abstract case catch class def do else enum
95-
erased extends false final finally for if implicit
96-
import lazy match new null object package private
97-
protected override return super sealed then throw trait
98-
true try type val var while with yield
95+
erased extends false final finally for given if
96+
implicit import instance lazy match new null object
97+
package private protected override return super sealed then
98+
throw trait true try type val var while
99+
with yield
99100
: = <- => <: :> # @
100101
```
101102

@@ -205,7 +206,7 @@ Catches ::= ‘catch’ Expr
205206
PostfixExpr ::= InfixExpr [id] PostfixOp(expr, op)
206207
InfixExpr ::= PrefixExpr
207208
| InfixExpr id [nl] InfixExpr InfixOp(expr, op, expr)
208-
| InfixExpr ‘with’ (InfixExpr | ParArgumentExprs)
209+
| InfixExpr ‘given’ (InfixExpr | ParArgumentExprs)
209210
PrefixExpr ::= [‘-’ | ‘+’ | ‘~’ | ‘!’] SimpleExpr PrefixOp(expr, op)
210211
SimpleExpr ::= ‘new’ (ConstrApp [TemplateBody] | TemplateBody) New(constr | templ)
211212
| BlockExpr
@@ -290,7 +291,7 @@ HkTypeParam ::= {Annotation} [‘+’ | ‘-’] (Id[HkTypeParamClause] |
290291
291292
ClsParamClauses ::= {ClsParamClause}
292293
ClsParamClause ::= [nl] ‘(’ [[FunArgMods] ClsParams] ‘)’
293-
| ‘with’ (‘(’ ([[FunArgMods] ClsParams] ‘)’ | ContextTypes)
294+
| ‘given’ (‘(’ ([[FunArgMods] ClsParams] ‘)’ | ContextTypes)
294295
ClsParams ::= ClsParam {‘,’ ClsParam}
295296
ClsParam ::= {Annotation} ValDef(mods, id, tpe, expr) -- point of mods on val/var
296297
[{Modifier} (‘val’ | ‘var’) | ‘inline’] Param
@@ -299,7 +300,7 @@ Param ::= id ‘:’ ParamType [‘=’ Expr]
299300
300301
DefParamClauses ::= {DefParamClause} [[nl] ‘(’ [FunArgMods] DefParams ‘)’]
301302
DefParamClause ::= [nl] ‘(’ [DefParams] ‘)’ | InstParamClause
302-
InstParamClause ::= ‘with’ (‘(’ [DefParams] ‘)’ | ContextTypes)
303+
InstParamClause ::= ‘given’ (‘(’ [DefParams] ‘)’ | ContextTypes)
303304
DefParams ::= DefParam {‘,’ DefParam}
304305
DefParam ::= {Annotation} [‘inline’] Param ValDef(mods, id, tpe, expr) -- point of mods at id.
305306
ContextTypes ::= RefinedType {‘,’ RefinedType}
Lines changed: 25 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,68 @@
11
---
22
layout: doc-page
3-
title: "Context Parameters and Arguments"
3+
title: "Implicit Parameters and Arguments"
44
---
55

6-
Context parameters are the name of a new syntax for implicit parameters that aligns definition and call syntax. Parameter definitions
7-
and method arguments both follow a `with` connective. On the definition side, the old syntax
6+
A new syntax for implicit parameters aligns definition and call syntax. Parameter definitions and method arguments both follow a `given` keyword. On the definition side, the old syntax
87
```scala
98
def f(a: A)(implicit b: B)
109
```
1110
is now expressed as
1211
```scala
13-
def f(a: A) with (b: B)
12+
def f(a: A) given (b: B)
1413
```
1514
or, leaving out the parameter name,
1615
```scala
17-
def f(a: A) with B
16+
def f(a: A) given B
1817
```
1918
Implicit parameters defined with the new syntax are also called _context parameters_.
20-
They come with a matching syntax for applications: explicit arguments for context parameters are also given after a `with`.
19+
They come with a matching syntax for applications: explicit arguments for context parameters are also written after a `given`.
2120

2221
The following example shows shows three methods that each have a context parameter for `Ord[T]`.
2322
```scala
24-
def maximum[T](xs: List[T]) with Ord[T]: T =
23+
def maximum[T](xs: List[T]) given Ord[T]: T =
2524
xs.reduceLeft((x, y) => if (x < y) y else x)
2625

27-
def descending[T] with (asc: Ord[T]): Ord[T] = new Ord[T] {
26+
def descending[T] given (asc: Ord[T]): Ord[T] = new Ord[T] {
2827
def (x: T) compareTo (y: T) = asc.compareTo(y)(x)
2928
}
3029

31-
def minimum[T](xs: List[T]) with Ord[T] =
32-
maximum(xs) with descending
30+
def minimum[T](xs: List[T]) given Ord[T] =
31+
maximum(xs) given descending
3332
```
3433
The `minimum` method's right hand side passes `descending` as an explicit argument to `maximum(xs)`.
3534
But usually, explicit arguments for context parameters are be left out. For instance,
3635
given `xs: List[Int]`, the following calls are all possible (and they all normalize to the last one:)
3736
```scala
3837
maximum(xs)
39-
maximum(xs) with descending
40-
maximum(xs) with (descending with IntOrd)
38+
maximum(xs) given descending
39+
maximum(xs) given (descending given IntOrd)
4140
```
42-
Arguments for context parameters must be given using the `with` syntax. So the expression `maximum(xs)(descending)` would give a type error.
41+
Arguments for context parameters must use the `given` syntax. So the expression `maximum(xs)(descending)` would produce a type error.
4342

44-
The `with` connective is treated like an infix operator with the same precedence as other operators that start with a letter. The expression following a `with` may also be an argument list consisting of several implicit arguments separated by commas. If a tuple should be passed as a single implicit argument (probably an uncommon case), it has to be put in a pair of extra parentheses:
43+
The `given` connective is treated like an infix operator with the same precedence as other operators that start with a letter. The expression following a `given` may also be an argument list consisting of several implicit arguments separated by commas. If a tuple should be passed as a single implicit argument (probably an uncommon case), it has to be put in a pair of extra parentheses:
4544
```scala
46-
def f with (x: A, y: B)
47-
f with (a, b)
45+
def f given (x: A, y: B)
46+
f given (a, b)
4847

49-
def g with (xy: (A, B))
50-
g with ((a, b))
48+
def g given (xy: (A, B))
49+
g given ((a, b))
5150
```
5251
Unlike existing implicit parameters, context parameters can be freely mixed with normal parameter lists.
5352
A context parameter may be followed by a normal parameter and _vice versa_. There can be several context parameter
5453
lists in a definition. Example:
5554
```scala
56-
def f with (u: Universe) (x: u.T) with Context = ...
55+
def f given (u: Universe) (x: u.T) given Context = ...
5756

58-
instance global for Universe { type T = String ... }
59-
instance ctx for Context { ... }
57+
instance global of Universe { type T = String ... }
58+
instance ctx of Context { ... }
6059
```
6160
Then the following calls are all valid (and normalize to the last one)
6261
```scala
6362
f("abc")
64-
(f with global)("abc")
65-
f("abc") with ctx
66-
(f with global)("abc") with ctx
63+
(f given global)("abc")
64+
f("abc") given ctx
65+
(f given global)("abc") given ctx
6766
```
6867
Context parameters may be given either as a normal parameter list `(...)`
6968
or as a sequence of types. To distinguish the two, a leading `(` always indicates a parameter list.
@@ -81,9 +80,9 @@ def summon[T] with (x: T) = x
8180
Here is the new syntax of parameters and arguments seen as a delta from the [standard context free syntax of Scala 3](http://dotty.epfl.ch/docs/internals/syntax.html).
8281
```
8382
ClsParamClause ::= ...
84-
| ‘with’ (‘(’ [ClsParams] ‘)’ | ContextTypes)
83+
| ‘given’ (‘(’ [ClsParams] ‘)’ | ContextTypes)
8584
DefParamClause ::= ...
8685
| InstParamClause
8786
InfixExpr ::= ...
88-
| InfixExpr ‘with’ (InfixExpr | ParArgumentExprs)
87+
| InfixExpr ‘given’ (InfixExpr | ParArgumentExprs)
8988
```

docs/docs/reference/instances/discussion/motivation.md

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,16 @@ Can implicit function types help? Implicit function types allow to abstract over
3333

3434
### Alternative Design
3535

36-
`implicit` is a modifier that gets attached to various constructs. I.e. we talk about implicit vals, defs, objects, parameters, or arguments. This conveys mechanism rather than intent. What _is_ the intent that we want to convey? Ultimately it's "trade types for terms". The programmer specifies a type and the compiler fills in the term matching that type automatically. So the concept we are after would serve to express definitions that provide the canonical _instances_ for certain types.
37-
38-
The next sections elaborate such an alternative design. It consists of three proposals:
39-
40-
- A proposal to replace implicit _definitions_ by [instance definitions](./instance-defs.html).
41-
- A proposal for a [new syntax](./context-params.html) of implicit _parameters_ and their _arguments_.
42-
- A proposal to [replace all remaining usages](./replacing-implicits.html) of `implicit` in the language.
43-
44-
The first two proposals are independent of each other. The last one would work only if the first two are adopted.
45-
A [discussion page](./discussion.html) summarizes and evaluates the proposal.
36+
`implicit` is a modifier that gets attached to various constructs.
37+
I.e. we talk about implicit vals, defs, objects, parameters, or arguments.
38+
This conveys mechanism rather than intent. What _is_ the intent that we want to convey?
39+
Ultimately it's "trade types for terms". The programmer specifies a type and the compiler
40+
fills in the term matching that type automatically. So the concept we are after would
41+
serve to express definitions that provide the canonical _instances_ for certain types.
42+
43+
The next sections elaborate this alternative design. It consists of the following pages:
44+
45+
- a proposal to replace implicit _definitions_ by [instance definitions](./instance-defs.md),
46+
- a proposal for a [new syntax](./context-params.md) of implicit _parameters_ and their _arguments_,
47+
- updates to the syntax for [implicit function types and closures](./implicit-function-types.md),
48+
- a new way to express [implicit conversions](./implicit-conversions.md) as instances of a special trait,
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
---
2+
layout: doc-page
3+
title: "Implicit Conversions"
4+
---
5+
6+
Implicit conversions are defined by instances of the `scala.Conversion` class.
7+
This class is defined in package `scala` as follows:
8+
```scala
9+
abstract class Conversion[-T, +U] extends (T => U)
10+
```
11+
For example, here is an implicit conversion from `String` to `Token`:
12+
```scala
13+
instance of Conversion[String, Token] {
14+
def apply(str: String): Token = new KeyWord(str)
15+
}
16+
```
17+
An implicit conversion is applied automatically by the compiler in three situations:
18+
19+
1. If an expression `e` has type `T`, and `T` does not conform to the expression's expected type `S`.
20+
2. In a selection `e.m` with `e` of type `T`, but `T` defines no member `m`.
21+
3. In an application `e.m(args)` with `e` of type `T`, if ``T` does define
22+
some member(s) named `m`, but none of these members can be applied to the arguments `args`.
23+
24+
In the first case, the compiler looks in the implicit scope for a an instance of
25+
`scala.Conversion` that maps an argument of type `T` to type `S`. In the second and third
26+
case, it looks for an instance of `scala.Conversion` that maps an argument of type `T`
27+
to a type that defines a member `m` which can be applied to `args` if present.
28+
If such an instance `C` is found, the expression `e` is replaced by `C.apply(e)`.
29+
30+
## Examples
31+
32+
1. The `Predef` package contains "auto-boxing" conversions that map
33+
primitive number types to subclasses of `java.lang.Number`. For instance, the
34+
conversion from `Int` to `java.lang.Integer` can be defined as follows:
35+
```scala
36+
instance int2Integer of Conversion[Int, java.lang.Integer] {
37+
def apply(x: Int) = new java.lang.Integer(x)
38+
}
39+
```
40+
41+
2. The "magnet" pattern is sometimes used to express many variants of a method. Instead of defining overloaded versions of the method, one can also let the method take one or more arguments of specially defined "magnet" types, into which various argument types can be converted. E.g.
42+
```scala
43+
object Completions {
44+
45+
// The argument "magnet" type
46+
enum CompletionArg {
47+
case Error(s: String)
48+
case Response(f: Future[HttpResponse])
49+
case Status(code: Future[StatusCode])
50+
}
51+
object CompletionArg {
52+
53+
// conversions defining the possible arguments to pass to `complete`
54+
// these always come with CompletionArg
55+
// They can be invoked explicitly, e.g.
56+
//
57+
// CompletionArg.from(statusCode)
58+
59+
instance from of Conversion[String, CompletionArg] {
60+
def apply(s: String) = CompletionArg.Error(s)
61+
}
62+
instance from of Conversion[Future[HttpResponse], CompletionArg] {
63+
def apply(f: Future[HttpResponse]) = CompletionArg.Response(f)
64+
}
65+
instance from of Conversion[Future[StatusCode], CompletionArg] {
66+
def apply(code: Future[StatusCode]) = CompletionArg.Status(code)
67+
}
68+
}
69+
import CompletionArg._
70+
71+
def complete[T](arg: CompletionArg) = arg match {
72+
case Error(s) => ...
73+
case Response(f) => ...
74+
case Status(code) => ...
75+
}
76+
}
77+
```
78+
This setup is more complicated than simple overloading of `complete`, but it can still be useful if normal overloading is not available (as in the case above, since we cannot have two overloaded methods that take `Future[...]` arguments), or if normal overloading would lead to a combinatorial explosion of variants.

0 commit comments

Comments
 (0)