Skip to content

Commit b86e91d

Browse files
committed
Reworked proposal
- drop distinction between implicitly passed and witness parameters - drop implicit as a modifier - use `with` instead of prefix `.` as connective for context parameters and arguments - use `|=>` for implicit function types and implicit closures
1 parent 8fa54f5 commit b86e91d

File tree

7 files changed

+174
-176
lines changed

7 files changed

+174
-176
lines changed

docs/docs/internals/syntax.md

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,8 @@ ClassQualifier ::= ‘[’ id ‘]’
117117

118118
### Types
119119
```ebnf
120-
Type ::= [FunArgMods | ‘.’] FunArgTypes ‘=>’ Type Function(ts, t)
120+
Type ::= [FunArgMods] FunArgTypes ‘=>’ Type Function(ts, t)
121+
| FunArgMods ‘|=>’ Type
121122
| HkTypeParamClause ‘=>’ Type TypeLambda(ps, t)
122123
| MatchType
123124
| InfixType
@@ -156,7 +157,8 @@ TypeParamBounds ::= TypeBounds {‘<%’ Type} {‘:’ Type}
156157

157158
### Expressions
158159
```ebnf
159-
Expr ::= [FunArgMods | ‘.’] FunParams ‘=>’ Expr Function(args, expr), Function(ValDef([implicit], id, TypeTree(), EmptyTree), expr)
160+
Expr ::= [FunArgMods] FunParams ‘=>’ Expr Function(args, expr), Function(ValDef([implicit], id, TypeTree(), EmptyTree), expr)
161+
| FunParams ‘|=>’ Expr
160162
| Expr1
161163
BlockResult ::= [FunArgMods] FunParams ‘=>’ Block
162164
| Expr1
@@ -184,6 +186,7 @@ Catches ::= ‘catch’ Expr
184186
PostfixExpr ::= InfixExpr [id] PostfixOp(expr, op)
185187
InfixExpr ::= PrefixExpr
186188
| InfixExpr id [nl] InfixExpr InfixOp(expr, op, expr)
189+
| InfixExpr ‘with’ (InfixExpr | ParArgumentExprs)
187190
PrefixExpr ::= [‘-’ | ‘+’ | ‘~’ | ‘!’] SimpleExpr PrefixOp(expr, op)
188191
SimpleExpr ::= ‘new’ Template New(templ)
189192
| BlockExpr
@@ -198,7 +201,6 @@ SimpleExpr1 ::= Literal
198201
| SimpleExpr ‘.’ id Select(expr, id)
199202
| SimpleExpr (TypeArgs | NamedTypeArgs) TypeApply(expr, args)
200203
| SimpleExpr1 ArgumentExprs Apply(expr, args)
201-
| SimpleExpr1 ‘.’ ParArgumentExprs
202204
| XmlExpr
203205
ExprsInParens ::= ExprInParens {‘,’ ExprInParens}
204206
ExprInParens ::= PostfixExpr ‘:’ Type
@@ -266,17 +268,15 @@ HkTypeParam ::= {Annotation} [‘+’ | ‘-’] (Id[HkTypeParamClause] |
266268
TypeBounds
267269
268270
ClsParamClauses ::= {ClsParamClause} [[nl] ‘(’ [FunArgMods] ClsParams ‘)’]
269-
ClsParamClause ::= [nl] ‘(’ [ClsParams] ‘)’
270-
| ‘.’ ‘(’ ClsParams ‘)’
271+
ClsParamClause ::= [nl | ‘with’] ‘(’ [ClsParams] ‘)’
271272
ClsParams ::= ClsParam {‘,’ ClsParam}
272273
ClsParam ::= {Annotation} ValDef(mods, id, tpe, expr) -- point of mods on val/var
273274
[{Modifier} (‘val’ | ‘var’) | ‘inline’] Param
274275
Param ::= id ‘:’ ParamType [‘=’ Expr]
275276
| INT
276277
277278
DefParamClauses ::= {DefParamClause} [[nl] ‘(’ [FunArgMods] DefParams ‘)’]
278-
DefParamClause ::= [nl] ‘(’ [DefParams] ‘)’
279-
| ‘.’ ‘(’ DefParams ‘)’
279+
DefParamClause ::= [nl | ‘with’] ‘(’ [DefParams] ‘)’
280280
ExtParamClause ::= [nl] ‘(’ ‘this’ DefParam ‘)’
281281
DefParams ::= DefParam {‘,’ DefParam}
282282
DefParam ::= {Annotation} [‘inline’] Param ValDef(mods, id, tpe, expr) -- point of mods at id.
@@ -347,10 +347,10 @@ ClassConstr ::= [ClsTypeParamClause] [ConstrMods] ClsParamClauses
347347
ConstrMods ::= {Annotation} [AccessModifier]
348348
ObjectDef ::= id TemplateOpt ModuleDef(mods, name, template) // no constructor
349349
EnumDef ::= id ClassConstr [‘extends’ [ConstrApps]] EnumBody EnumDef(mods, name, tparams, template)
350-
WitnessDef ::= [id] [DefTypeParamClause] [‘with’ WitnessParams]
351-
[‘for’ [ConstrApps]] TemplateBody
352-
| id [DefTypeParamClause] [‘with’ WitnessParams]
353-
[‘for’ Type] [‘=’ Expr]
350+
WitnessDef ::= [id] WitnessParams [‘for’ ConstrApps] [TemplateBody]
351+
| id WitnessParams ‘:’ Type ‘=’ Expr
352+
| id ‘=’ Expr
353+
WitnessParams ::= [DefTypeParamClause] {‘with’ ‘(’ [DefParams] ‘)}
354354
TemplateOpt ::= [‘extends’ Template | [nl] TemplateBody]
355355
Template ::= ConstrApps [TemplateBody] | TemplateBody Template(constr, parents, self, stats)
356356
ConstrApps ::= ConstrApp {‘with’ ConstrApp}

docs/docs/reference/witnesses/discussion.md

Lines changed: 12 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,14 @@ title: "Discussion"
55

66
## Summary
77

8-
The witness proposal consists of three main parts:
8+
The witness proposal consists of two main parts:
99

1010
- Define a new [high-level syntax for witnesses](./witnesses.html) that works out better the intent underlying implicit definitions.
11-
- Split the two meanings of implicit parameters into [separate concepts](./witness-params.html). One concept specifies that a parameter is implicitly applied, the other makes the parameter available as a witness.
12-
- Allow `witness` as a [modifier](./witness-modifier.html) to replace remaining use cases for implicit definitions and to provide a lower level syntax into which witness definitions (with `witness` as a subject) can be translated.
11+
- Define a [new syntax for implicit parameters](./witness-params.html) that aligns formal parameters and arguments.
1312

1413
## Other Uses of `implicit`
1514

16-
The only use cases that are not yet covered by the proposal are implicit classes and implicit conversions. We do not propose to use `witness` in place of `implicit` for these, since that would bring back the uncomfortable similarity between implicit conversions and parameterized implicit aliases. However, there is a way to drop implicit conversions entirely. Scala 3 already [defines](https://github.com/lampepfl/dotty/pull/2065) a class `ImplicitConverter` whose instances are available as implicit conversions.
15+
The only use cases that are not yet covered by the proposal are implicit conversions and implicit classes. We do not propose to use `witness` in place of `implicit` for these, since that would bring back the uncomfortable similarity between implicit conversions and parameterized implicit aliases. However, there is a way to drop implicit conversions entirely. Scala 3 already [defines](https://github.com/lampepfl/dotty/pull/2065) a class `ImplicitConverter` whose instances are available as implicit conversions.
1716
```scala
1817
abstract class ImplicitConverter[-T, +U] extends Function1[T, U]
1918
```
@@ -37,9 +36,10 @@ This is a rather sweeping proposal, which will affect most Scala code. Here are
3736

3837
The witness definition syntax makes the definition of implicit instances clearer and more concise. People have repeatedly asked for specific "typeclass syntax" in Scala. I believe that witnesses together with extension methods address this ask quite well.
3938

40-
Probably the most far reaching and contentious changes affect implicit parameters. There might be resistance to change, because the existing scheme seems to work "well enough". However, I believe there is a price to pay for the status quo. The need to write `.apply` occasionally to force implicit arguments is already bad. Worse is the fact that implicits always have to come last, which makes useful program patterns much more cumbersome than before and makes the language less regular. Also problematic is the
41-
lack of scope control for implicit parameters which caused the invention of macro-based libraries such as MacWire for what looks like an ideal task for implicits. Without the
42-
changes proposed here, dependency injection in the style of MacWire will no longer be possible in Scala 3, since whitebox macros are going away.
39+
A contentious point is whether we want abstract and alias witnesses. As an alternative, would could also keep the current syntax for implicit vals and defs, which can express the same concepts. The main advantage to introduce abstract and alias witnesses is that it would
40+
allow us to drop implicit definitions altogether.
41+
42+
Probably the most far reaching and contentious changes affect implicit parameters. There might be resistance to change, because the existing scheme seems to work "well enough". However, I believe there is a price to pay for the status quo. The need to write `.apply` occasionally to force implicit arguments is already bad. Worse is the fact that implicits always have to come last, which makes useful program patterns much more cumbersome than before and makes the language less regular.
4343

4444
Several alternatives to the proposed syntax changes for implicit parameters were considered:
4545

@@ -50,15 +50,8 @@ Several alternatives to the proposed syntax changes for implicit parameters were
5050
construct an extensive explicit argument tree to figure out what went wrong with a missing
5151
implicit. Another issue is that migration from old to new scheme would be tricky and
5252
would likely take multiple language versions.
53-
3. The design presented here, but with `implicit` instead of `witness` as the modifier for
54-
parameters. This is closer to the status quo, but migration presents problems: At what point will an `implicit` modifier stop having the old meaning and acquire the new one? Using `witness` instead of `implicit` solves that problem because old and new styles can coexist and it is always clear which is which.
55-
4. Don't split the meanings of implicitly passed parameters and witness parameters. Use prefix ‘.’ as a syntax
56-
for both meanings together. This is more concise and relieves the programmer from having to choose
57-
which combination of functionality is desired. On the other hand, this does not support some
58-
use patterns such as MacWire style dependency injection. Also, the prefix ‘.’ syntax is maybe
59-
a bit too inconspicuous at the parameter definition site, even though it works very well at the
60-
function application site.
61-
5. As in 4., but using `witness` or `implicit` as the syntax that marks an implicitly passed witness parameter.
62-
This breaks the correspondence between function abstraction and application syntax.
63-
64-
Once we have high-level witness definitions and witness parameters it's a small step to convert most the remaining uses of `implicit` to `witness` as a modifier. There are no stringent technical reasons for doing so, but it looks more consistent with the other changes.
53+
3. Split the meanings of implicitly passed parameters and witness parameters. Use prefix ‘.’ as a syntax to indicate that an argument for a parameter can be passed explicitly. Use
54+
`witness` as a parameter modifier to indicate that the parameter is available as a witness.
55+
This scheme admits some new patterns, such as an explicit parameter that can be
56+
used as a witness, or an implicitly passed parameter that is not a witness itself.
57+
But the syntax looks unfamiliar and suffers from the choice paradox.

docs/docs/reference/witnesses/motivation.md

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,7 @@ Fourth, the syntax of implicit parameters has also some shortcomings. It starts
2525
```scala
2626
def currentMap(implicit ctx: Context): Map[String, Int]
2727
```
28-
one cannot write `currentMap("abc")` since the string "abc" is taken as explicit argument to the implicit `ctx` parameter. One has to write `currentMap.apply("abc")` instead, which is awkward and irregular. For the same reason, a method definition can only have one implicit parameter section and it must always come last. This restriction not only reduces orthogonality, but also prevents some useful program constructs, such as a method with a regular parameter whose type depends on an implicit value. Finally, it's also a bit annoying that implicit parameters must have a name, even though in most cases that name is never referenced.
29-
30-
Fifth, there is a lack of mechanism to contain the scope of implicits. Specifically, implicit parameters are always themselves implicit values in the body of the class or method in which they are defined. There are situations where one would like the parameter to a method or a class to be passed implicitly without that parameter becoming a candidate for further implicits in the body. The popular dependency injection framework MacWire exists precisely because such fine grained control of implicit scope is currently not available.
28+
one cannot write `currentMap("abc")` since the string "abc" is taken as explicit argument to the implicit `ctx` parameter. One has to write `currentMap.apply("abc")` instead, which is awkward and irregular. For the same reason, a method definition can only have one implicit parameter section and it must always come last. This restriction not only reduces orthogonality, but also prevents some useful program constructs, such as a method with a regular parameter whose type depends on an implicit value. Finally, it's also a bit annoying that implicit parameters must have a name, even though in many cases that name is never referenced.
3129

3230
None of the shortcomings is fatal, after all implicits are very widely used, and many libraries and applications rely on them. But together, they make code using implicits more cumbersome and less clear than it could be.
3331

@@ -37,12 +35,9 @@ Can implicit function types help? Implicit function types allow to abstract over
3735

3836
`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.
3937

40-
I believe a good name for is concept is _witness_. A term is a witness for a type by defining an implicit instance of this type. It's secondary whether this instance takes the form of a `val` or `object` or whether it is a method. It would be better to have a uniform syntax for all of these kinds of instances.
38+
I believe a good name for this concept is _witness_. A term is a witness for a type if it is an implicit instance of this type. It is secondary whether this instance takes the form of a `val` or `object` or whether it is a method. It would be better to have a uniform syntax for all of these kinds of instances.
4139

42-
The next sections elaborate such an alternative design. It consists of three proposals which are independent of each other:
40+
The next sections elaborate such an alternative design. It consists of two proposals which are independent of each other:
4341

44-
- A proposal to replace implicit _definitions_ by [witness definitions](./witnesses.html)
45-
.
42+
- A proposal to replace implicit _definitions_ by [witness definitions](./witnesses.html).
4643
- A proposal for a [new syntax](./witness-params.html) of implicit _parameters_ and their _arguments_.
47-
48-
- A proposal to replace most or all remaining uses of the `implicit` modifier by a new `witness` [modifier](./witness-modifier.html).

docs/docs/reference/witnesses/witness-modifier.md

Lines changed: 0 additions & 58 deletions
This file was deleted.

0 commit comments

Comments
 (0)