Skip to content

Commit

Permalink
Reworked proposal
Browse files Browse the repository at this point in the history
 - 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
  • Loading branch information
odersky committed Nov 16, 2018
1 parent 8fa54f5 commit b86e91d
Show file tree
Hide file tree
Showing 7 changed files with 174 additions and 176 deletions.
22 changes: 11 additions & 11 deletions docs/docs/internals/syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,8 @@ ClassQualifier ::= ‘[’ id ‘]’

### Types
```ebnf
Type ::= [FunArgMods | ‘.’] FunArgTypes ‘=>’ Type Function(ts, t)
Type ::= [FunArgMods] FunArgTypes ‘=>’ Type Function(ts, t)
| FunArgMods ‘|=>’ Type
| HkTypeParamClause ‘=>’ Type TypeLambda(ps, t)
| MatchType
| InfixType
Expand Down Expand Up @@ -156,7 +157,8 @@ TypeParamBounds ::= TypeBounds {‘<%’ Type} {‘:’ Type}

### Expressions
```ebnf
Expr ::= [FunArgMods | ‘.’] FunParams ‘=>’ Expr Function(args, expr), Function(ValDef([implicit], id, TypeTree(), EmptyTree), expr)
Expr ::= [FunArgMods] FunParams ‘=>’ Expr Function(args, expr), Function(ValDef([implicit], id, TypeTree(), EmptyTree), expr)
| FunParams ‘|=>’ Expr
| Expr1
BlockResult ::= [FunArgMods] FunParams ‘=>’ Block
| Expr1
Expand Down Expand Up @@ -184,6 +186,7 @@ Catches ::= ‘catch’ Expr
PostfixExpr ::= InfixExpr [id] PostfixOp(expr, op)
InfixExpr ::= PrefixExpr
| InfixExpr id [nl] InfixExpr InfixOp(expr, op, expr)
| InfixExpr ‘with’ (InfixExpr | ParArgumentExprs)
PrefixExpr ::= [‘-’ | ‘+’ | ‘~’ | ‘!’] SimpleExpr PrefixOp(expr, op)
SimpleExpr ::= ‘new’ Template New(templ)
| BlockExpr
Expand All @@ -198,7 +201,6 @@ SimpleExpr1 ::= Literal
| SimpleExpr ‘.’ id Select(expr, id)
| SimpleExpr (TypeArgs | NamedTypeArgs) TypeApply(expr, args)
| SimpleExpr1 ArgumentExprs Apply(expr, args)
| SimpleExpr1 ‘.’ ParArgumentExprs
| XmlExpr
ExprsInParens ::= ExprInParens {‘,’ ExprInParens}
ExprInParens ::= PostfixExpr ‘:’ Type
Expand Down Expand Up @@ -266,17 +268,15 @@ HkTypeParam ::= {Annotation} [‘+’ | ‘-’] (Id[HkTypeParamClause] |
TypeBounds
ClsParamClauses ::= {ClsParamClause} [[nl] ‘(’ [FunArgMods] ClsParams ‘)’]
ClsParamClause ::= [nl] ‘(’ [ClsParams] ‘)’
| ‘.’ ‘(’ ClsParams ‘)’
ClsParamClause ::= [nl | ‘with’] ‘(’ [ClsParams] ‘)’
ClsParams ::= ClsParam {‘,’ ClsParam}
ClsParam ::= {Annotation} ValDef(mods, id, tpe, expr) -- point of mods on val/var
[{Modifier} (‘val’ | ‘var’) | ‘inline’] Param
Param ::= id ‘:’ ParamType [‘=’ Expr]
| INT
DefParamClauses ::= {DefParamClause} [[nl] ‘(’ [FunArgMods] DefParams ‘)’]
DefParamClause ::= [nl] ‘(’ [DefParams] ‘)’
| ‘.’ ‘(’ DefParams ‘)’
DefParamClause ::= [nl | ‘with’] ‘(’ [DefParams] ‘)’
ExtParamClause ::= [nl] ‘(’ ‘this’ DefParam ‘)’
DefParams ::= DefParam {‘,’ DefParam}
DefParam ::= {Annotation} [‘inline’] Param ValDef(mods, id, tpe, expr) -- point of mods at id.
Expand Down Expand Up @@ -347,10 +347,10 @@ ClassConstr ::= [ClsTypeParamClause] [ConstrMods] ClsParamClauses
ConstrMods ::= {Annotation} [AccessModifier]
ObjectDef ::= id TemplateOpt ModuleDef(mods, name, template) // no constructor
EnumDef ::= id ClassConstr [‘extends’ [ConstrApps]] EnumBody EnumDef(mods, name, tparams, template)
WitnessDef ::= [id] [DefTypeParamClause] [‘with’ WitnessParams]
[‘for’ [ConstrApps]] TemplateBody
| id [DefTypeParamClause] [‘with’ WitnessParams]
[‘for’ Type] [‘=’ Expr]
WitnessDef ::= [id] WitnessParams [‘for’ ConstrApps] [TemplateBody]
| id WitnessParams ‘:’ Type ‘=’ Expr
| id ‘=’ Expr
WitnessParams ::= [DefTypeParamClause] {‘with’ ‘(’ [DefParams] ‘)}
TemplateOpt ::= [‘extends’ Template | [nl] TemplateBody]
Template ::= ConstrApps [TemplateBody] | TemplateBody Template(constr, parents, self, stats)
ConstrApps ::= ConstrApp {‘with’ ConstrApp}
Expand Down
31 changes: 12 additions & 19 deletions docs/docs/reference/witnesses/discussion.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,14 @@ title: "Discussion"

## Summary

The witness proposal consists of three main parts:
The witness proposal consists of two main parts:

- Define a new [high-level syntax for witnesses](./witnesses.html) that works out better the intent underlying implicit definitions.
- 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.
- 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.
- Define a [new syntax for implicit parameters](./witness-params.html) that aligns formal parameters and arguments.

## Other Uses of `implicit`

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.
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.
```scala
abstract class ImplicitConverter[-T, +U] extends Function1[T, U]
```
Expand All @@ -37,9 +36,10 @@ This is a rather sweeping proposal, which will affect most Scala code. Here are

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.

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
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
changes proposed here, dependency injection in the style of MacWire will no longer be possible in Scala 3, since whitebox macros are going away.
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
allow us to drop implicit definitions altogether.

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.

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

Expand All @@ -50,15 +50,8 @@ Several alternatives to the proposed syntax changes for implicit parameters were
construct an extensive explicit argument tree to figure out what went wrong with a missing
implicit. Another issue is that migration from old to new scheme would be tricky and
would likely take multiple language versions.
3. The design presented here, but with `implicit` instead of `witness` as the modifier for
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.
4. Don't split the meanings of implicitly passed parameters and witness parameters. Use prefix ‘.’ as a syntax
for both meanings together. This is more concise and relieves the programmer from having to choose
which combination of functionality is desired. On the other hand, this does not support some
use patterns such as MacWire style dependency injection. Also, the prefix ‘.’ syntax is maybe
a bit too inconspicuous at the parameter definition site, even though it works very well at the
function application site.
5. As in 4., but using `witness` or `implicit` as the syntax that marks an implicitly passed witness parameter.
This breaks the correspondence between function abstraction and application syntax.

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.
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
`witness` as a parameter modifier to indicate that the parameter is available as a witness.
This scheme admits some new patterns, such as an explicit parameter that can be
used as a witness, or an implicitly passed parameter that is not a witness itself.
But the syntax looks unfamiliar and suffers from the choice paradox.
13 changes: 4 additions & 9 deletions docs/docs/reference/witnesses/motivation.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,7 @@ Fourth, the syntax of implicit parameters has also some shortcomings. It starts
```scala
def currentMap(implicit ctx: Context): Map[String, Int]
```
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.

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.
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.

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.

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

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

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.
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.

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

- A proposal to replace implicit _definitions_ by [witness definitions](./witnesses.html)
.
- A proposal to replace implicit _definitions_ by [witness definitions](./witnesses.html).
- A proposal for a [new syntax](./witness-params.html) of implicit _parameters_ and their _arguments_.

- A proposal to replace most or all remaining uses of the `implicit` modifier by a new `witness` [modifier](./witness-modifier.html).
58 changes: 0 additions & 58 deletions docs/docs/reference/witnesses/witness-modifier.md

This file was deleted.

Loading

0 comments on commit b86e91d

Please sign in to comment.