Skip to content

Commit ce73a94

Browse files
committed
Implement and test "replacing-implicits" chapter.
1 parent 977f8ff commit ce73a94

File tree

6 files changed

+102
-33
lines changed

6 files changed

+102
-33
lines changed

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

+16-6
Original file line numberDiff line numberDiff line change
@@ -2464,6 +2464,7 @@ object Parsers {
24642464

24652465
/** WitnessDef ::= [id] WitnessParams [‘for’ ConstrApps] [TemplateBody]
24662466
* | id WitnessParams ‘:’ Type ‘=’ Expr
2467+
* | id ‘:’ ‘=>’ Type ‘=’ Expr
24672468
* | id ‘=’ Expr
24682469
* WitnessParams ::= [DefTypeParamClause] {‘with’ ‘(’ [DefParams] ‘)}
24692470
*/
@@ -2478,7 +2479,7 @@ object Parsers {
24782479
}
24792480
else Nil
24802481
newLineOptWhenFollowedBy(LBRACE)
2481-
if ((name.isEmpty || parents.isEmpty) && in.token != LBRACE)
2482+
if (name.isEmpty && in.token != LBRACE)
24822483
syntaxErrorOrIncomplete(ExpectedTokenButFound(LBRACE, in.token))
24832484
var mods1 = addMod(mods, witnessMod)
24842485
val wdef =
@@ -2488,17 +2489,26 @@ object Parsers {
24882489
else TypeDef(name.toTypeName, templ)
24892490
}
24902491
else {
2492+
var byName = false
2493+
val tpt =
2494+
if (in.token == COLON) {
2495+
in.nextToken()
2496+
if (in.token == ARROW && tparams.isEmpty && vparamss.isEmpty) {
2497+
in.nextToken()
2498+
byName = true
2499+
}
2500+
toplevelTyp()
2501+
}
2502+
else TypeTree()
2503+
if (tpt.isEmpty && in.token != EQUALS)
2504+
syntaxErrorOrIncomplete(ExpectedTokenButFound(LBRACE, in.token))
24912505
val rhs =
24922506
if (in.token == EQUALS) {
24932507
in.nextToken()
24942508
expr()
24952509
}
24962510
else EmptyTree
2497-
val tpt = constrAppsToType(parents)
2498-
if (tparams.isEmpty && vparamss.isEmpty) {
2499-
mods1 |= Lazy
2500-
ValDef(name, tpt, rhs)
2501-
}
2511+
if (tparams.isEmpty && vparamss.isEmpty && !byName) ValDef(name, tpt, rhs)
25022512
else DefDef(name, tparams, vparamss, tpt, rhs)
25032513
}
25042514
finalizeDef(wdef, mods1, start)

docs/docs/internals/syntax.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -347,8 +347,9 @@ ObjectDef ::= id TemplateOpt
347347
EnumDef ::= id ClassConstr [‘extends’ [ConstrApps]] EnumBody EnumDef(mods, name, tparams, template)
348348
WitnessDef ::= [id] WitnessParams [‘for’ ConstrApps] [TemplateBody]
349349
| id WitnessParams ‘:’ Type ‘=’ Expr
350+
| id ‘:’ ‘=>’ Type ‘=’ Expr
350351
| id ‘=’ Expr
351-
WitnessParams ::= [DefTypeParamClause] {‘with’ ‘(’ [DefParams] ‘)}
352+
WitnessParams ::= [DefTypeParamClause] {‘with’ ‘(’ [DefParams] ‘)'}
352353
TemplateOpt ::= [‘extends’ Template | [nl] TemplateBody]
353354
Template ::= ConstrApps [TemplateBody] | TemplateBody Template(constr, parents, self, stats)
354355
ConstrApps ::= ConstrApp {‘with’ ConstrApp}

docs/docs/reference/witnesses/replacing-implicits.md

+5-3
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ Further examples of alias witnesses:
4040
```scala
4141
witness ctx = outer.ctx
4242
witness ctx: Context = outer.ctx
43-
witness byNameCtx(): Context = outer.ctx
43+
witness byNameCtx: => Context = outer.ctx
4444
witness f[T]: C[T] = new C[T]
4545
witness g with (ctx: Context): D = new D(ctx)
4646
```
@@ -55,14 +55,15 @@ witness listOrd[T: Ord]: Ord[List[T]] = new ListOrd[T]
5555
The result type of a alias witness is mandatory unless the witness definition
5656
occurs as a statement in a block and lacks any type or value parameters. This corresponds to the same restriction for implicit vals in Dotty.
5757

58-
Abstract witnesses are equivalent to abstract implicit defs. Alias witnesses are equivalent to implicit defs if they are parameterized or for implicit vals otherwise. For instance, the witnesses defined so far in this section are equivalent to:
58+
Abstract witnesses are equivalent to abstract implicit defs. Alias witnesses are equivalent to implicit defs if they are parameterized or have a `=> T` result type.
59+
They translate to implicit vals otherwise. For instance, the witnesses defined so far in this section are equivalent to:
5960
```scala
6061
implicit def symDeco: SymDeco
6162
implicit val symDeco: SymDeco = compilerSymOps
6263

6364
implicit val ctx = outer.ctx
6465
implicit val ctx: Context = outer.ctx
65-
implicit def byNameCtx(): Ctx = outer.ctx
66+
implicit def byNameCtx: Ctx = outer.ctx
6667
implicit def f[T]: C[T] = new C[T]
6768
implicit def g(implicit ctx: Context): D = new D(ctx)
6869

@@ -107,6 +108,7 @@ The syntax changes for this page are summarized as follows:
107108
```
108109
WitnessDef ::= ...
109110
| id WitnessParams ‘:’ Type ‘=’ Expr
111+
| id ‘:’ ‘=>’ Type ‘=’ Expr
110112
| id ‘=’ Expr
111113
```
112114
In addition, the `implicit` modifier is removed together with all [productions]((http://dotty.epfl.ch/docs/internals/syntax.html) that reference it.

docs/docs/reference/witnesses/witnesses.md

+24-21
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,18 @@ Witnesses provide a concise and uniform syntax for defining implicit values. Exa
77

88
```scala
99
trait Ord[T] {
10-
def compareTo(this x: T)(y: T): Int
11-
def < (this x: T)(y: T) = x.compareTo(y) < 0
12-
def > (this x: T)(y: T) = x.compareTo(y) > 0
10+
def (x: T) compareTo (y: T): Int
11+
def (x: T) < (y: T) = x.compareTo(y) < 0
12+
def (x: T) > (y: T) = x.compareTo(y) > 0
1313
}
1414

1515
witness IntOrd for Ord[Int] {
16-
def compareTo(this x: Int)(y: Int) =
16+
def (x: Int) compareTo (y: Int) =
1717
if (x < y) -1 else if (x > y) +1 else 0
1818
}
1919

2020
witness ListOrd[T: Ord] for Ord[List[T]] {
21-
def compareTo(this xs: List[T])(ys: List[T]): Int = (xs, ys) match {
21+
def (xs: List[T]) compareTo (ys: List[T]): Int = (xs, ys) match {
2222
case (Nil, Nil) => 0
2323
case (Nil, _) => -1
2424
case (_, Nil) => +1
@@ -32,12 +32,12 @@ witness ListOrd[T: Ord] for Ord[List[T]] {
3232
Witness can be seen as shorthands for what is currently expressed as implicit definitions. The witnesses above could also have been formulated as implicits as follows:
3333
```scala
3434
implicit object IntOrd extends Ord[Int] {
35-
def compareTo(this x: Int)(y: Int) =
35+
def (x: Int) compareTo (y: Int) =
3636
if (x < y) -1 else if (x > y) +1 else 0
3737
}
3838

3939
class ListOrd[T: Ord] extends Ord[List[T]] {
40-
def compareTo(this xs: List[T])(ys: List[T]): Int = (xs, ys) match {
40+
def (xs: List[T]) compareTo (ys: List[T]): Int = (xs, ys) match {
4141
case (Nil, Nil) => 0
4242
case (Nil, _) => -1
4343
case (_, Nil) => +1
@@ -60,14 +60,14 @@ Witnesses can also be defined without a `for` clause. A typical application is t
6060

6161
```scala
6262
witness StringOps {
63-
def longestStrings(this xs: Seq[String]): Seq[String] = {
63+
def (xs: Seq[String]) longestStrings: Seq[String] = {
6464
val maxLength = xs.map(_.length).max
6565
xs.filter(_.length == maxLength)
6666
}
6767
}
6868

6969
witness ListOps {
70-
def second[T](this xs: List[T]) = xs.tail.head
70+
def (xs: List[T]) second[T] = xs.tail.head
7171
}
7272
```
7373
Witnesses like these translate to `implicit` objects without an extends clause.
@@ -80,7 +80,7 @@ witness for Ord[Int] { ... }
8080
witness [T: Ord] for Ord[List[T]] { ... }
8181

8282
witness {
83-
def second[T](this xs: List[T]) = xs.tail.head
83+
def (xs: List[T]) second[T] = xs.tail.head
8484
}
8585
```
8686
If the name of a witness is missing, the compiler will synthesize a name from
@@ -92,11 +92,11 @@ extension method. Details remain to be specified.
9292
A witness can depend on another witness being defined. For instance:
9393
```scala
9494
trait Convertible[From, To] {
95-
def convert (this x: From): To
95+
def (x: From) convert: To
9696
}
9797

9898
witness [From, To] with (c: Convertible[From, To]) for Convertible[List[From], List[To]] {
99-
def convert (this x: List[From]): List[To] = x.map(c.convert)
99+
def (x: List[From]) convert: List[To] = x.map(c.convert)
100100
}
101101
```
102102

@@ -105,7 +105,7 @@ The `with` clause in a witness defines required witnesses. The witness for `Conv
105105
```scala
106106
class Convertible_List_List_witness[From, To](implicit c: Convertible[From, To])
107107
extends Convertible[List[From], List[To]] {
108-
def convert (this x: List[From]): List[To] = x.map(c.convert)
108+
def (x: List[From]) convert: List[To] = x.map(c.convert)
109109
}
110110
implicit def Convertible_List_List_witness[From, To](implicit c: Convertible[From, To])
111111
: Convertible[List[From], List[To]] =
@@ -132,42 +132,45 @@ Semigroups and monoids:
132132

133133
```scala
134134
trait SemiGroup[T] {
135-
def combine(this x: T)(y: T): T
135+
def (x: T) combine (y: T): T
136136
}
137137
trait Monoid[T] extends SemiGroup[T] {
138138
def unit: T
139139
}
140+
object Monoid {
141+
def apply[T: Monoid] = summon[Monoid[T]]
142+
}
140143

141144
witness for Monoid[String] {
142-
def combine(this x: String)(y: String): String = x.concat(y)
145+
def (x: String) combine (y: String): String = x.concat(y)
143146
def unit: String = ""
144147
}
145148

146149
def sum[T: Monoid](xs: List[T]): T =
147-
xs.foldLeft(summon[Monoid[T]].unit)(_.combine(_))
150+
xs.foldLeft(Monoid[T].unit)(_.combine(_))
148151
```
149152
Functors and monads:
150153
```scala
151154
trait Functor[F[_]] {
152-
def map[A, B](this x: F[A])(f: A => B): F[B]
155+
def (x: F[A]) map[A, B] (f: A => B): F[B]
153156
}
154157

155158
trait Monad[F[_]] extends Functor[F] {
156-
def flatMap[A, B](this x: F[A])(f: A => F[B]): F[B]
157-
def map[A, B](this x: F[A])(f: A => B) = x.flatMap(f `andThen` pure)
159+
def (x: F[A]) flatMap[A, B] (f: A => F[B]): F[B]
160+
def (x: F[A]) map[A, B] (f: A => B) = x.flatMap(f `andThen` pure)
158161

159162
def pure[A](x: A): F[A]
160163
}
161164

162165
witness ListMonad for Monad[List] {
163-
def flatMap[A, B](this xs: List[A])(f: A => List[B]): List[B] =
166+
def (xs: List[A]) flatMap[A, B] (f: A => List[B]): List[B] =
164167
xs.flatMap(f)
165168
def pure[A](x: A): List[A] =
166169
List(x)
167170
}
168171

169172
witness ReaderMonad[Ctx] for Monad[[X] => Ctx => X] {
170-
def flatMap[A, B](this r: Ctx => A)(f: A => Ctx => B): Ctx => B =
173+
def (r: Ctx => A) flatMap[A, B] (f: A => Ctx => B): Ctx => B =
171174
ctx => f(r(ctx))(ctx)
172175
def pure[A](x: A): Ctx => A =
173176
ctx => x

library/src/dotty/DottyPredef.scala

+2
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,6 @@ object DottyPredef {
4040
@forceInline final def implicitly[T](implicit ev: T): T = ev
4141

4242
@forceInline def locally[T](body: => T): T = body
43+
44+
@forceInline def summon[T](implicit x: T) = x
4345
}

tests/pos/reference/witnesses.scala

+53-2
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,6 @@ class Common {
2727

2828
def pure[A](x: A): F[A]
2929
}
30-
31-
inline def summon[T] with (x: T) = x
3230
}
3331

3432
object Witnesses extends Common {
@@ -99,6 +97,59 @@ object Witnesses extends Common {
9997
class A
10098
class B
10199
val ab: (x: A, y: B) |=> Int = (a: A, b: B) |=> 22
100+
101+
trait TastyAPI {
102+
type Symbol
103+
trait SymDeco {
104+
def name(this sym: Symbol): String
105+
}
106+
witness symDeco: SymDeco
107+
}
108+
object TastyImpl extends TastyAPI {
109+
type Symbol = String
110+
witness symDeco: SymDeco = new SymDeco {
111+
def name(this sym: Symbol) = sym
112+
}
113+
}
114+
115+
class D[T]
116+
117+
class C with (ctx: Context) {
118+
def f() = {
119+
locally {
120+
witness ctx = this.ctx
121+
println(summon[Context].value)
122+
}
123+
locally {
124+
lazy witness ctx = this.ctx
125+
println(summon[Context].value)
126+
}
127+
locally {
128+
witness ctx: Context = this.ctx
129+
println(summon[Context].value)
130+
}
131+
locally {
132+
witness ctx: => Context = this.ctx
133+
println(summon[Context].value)
134+
}
135+
locally {
136+
witness f[T]: D[T] = new D[T]
137+
println(summon[D[Int]])
138+
}
139+
locally {
140+
witness g with (ctx: Context): D[Int] = new D[Int]
141+
println(summon[D[Int]])
142+
}
143+
}
144+
}
145+
146+
class Token(str: String)
147+
148+
witness StringToToken for ImplicitConverter[String, Token] {
149+
def apply(str: String): Token = new Token(str)
150+
}
151+
152+
val x: Token = "if"
102153
}
103154

104155
object PostConditions {

0 commit comments

Comments
 (0)