You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/docs/reference/witnesses/motivation.md
+5-330Lines changed: 5 additions & 330 deletions
Original file line number
Diff line number
Diff line change
@@ -35,335 +35,10 @@ Can implicit function types help? Implicit function types allow to abstract over
35
35
36
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
37
38
-
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
39
-
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
40
-
such an alternative design.
41
-
42
-
43
-
```scala
44
-
traitOrd[T] {
45
-
defcompareTo(thisx: T)(y: T):Int
46
-
def< (thisx: T)(y: T) = x.compareTo(y) <0
47
-
def> (thisx: T)(y: T) = x.compareTo(y) >0
48
-
}
49
-
50
-
witness IntOrdforOrd[Int] {
51
-
defcompareTo(thisx: Int)(y: Int) =
52
-
if (x < y) -1elseif (x > y) +1else0
53
-
}
54
-
55
-
witness ListOrd[T:Ord] forOrd[List[T]] {
56
-
defcompareTo(thisxs: List[T])(ys: List[T]):Int= (xs, ys) match {
57
-
case (Nil, Nil) =>0
58
-
case (Nil, _) =>-1
59
-
case (_, Nil) =>+1
60
-
case (x :: xs1, y :: ys1) =>
61
-
valfst= x.compareTo(y)
62
-
if (fst !=0) fst else xs1.compareTo(ys1)
63
-
}
64
-
}
65
-
```
66
-
Witness are shorthands for implicit definitions. The winesses above could also have been
67
-
formulated as implicits as follows:
68
-
```scala
69
-
implicitobjectIntOrdextendsOrd[Int] {
70
-
defcompareTo(thisx: Int)(y: Int) =
71
-
if (x < y) -1elseif (x > y) +1else0
72
-
}
73
-
74
-
classListOrd[T:Ord] forOrd[List[T]] {
75
-
defcompareTo(thisxs: List[T])(ys: List[T]):Int= (xs, ys) match {
`CircleOps` adds an extension method `circumference` to values of class `Circle`. Like regular methods, extension methods can be invoked with infix `.`:
133
-
134
-
```scala
135
-
valcircle=Circle(0, 0, 1)
136
-
circle.circumference
137
-
```
138
-
139
-
Extension methods are methods that have a `this` modifier for the first parameter.
140
-
They can also be invoked as plain methods. So the following holds:
When is an extension method considered? There are two possibilities. The first (and recommended one) is by defining the extension method as a member of an implicit value. The method can then be used as an extension method wherever the implicit value is applicable. The second possibility is by making the extension method itself visible under a simple name, typically by importing it. As an example, consider an extension method `longestStrings` on `String`. We can either define it like this:
150
-
151
-
152
-
```scala
153
-
implicitobjectStringSeqOps1 {
154
-
deflongestStrings(thisxs: Seq[String]) = {
155
-
valmaxLength= xs.map(_.length).max
156
-
xs.filter(_.length == maxLength)
157
-
}
158
-
}
159
-
```
160
-
Then
161
-
```scala
162
-
List("here", "is", "a", "list").longestStrings
163
-
```
164
-
is legal everywhere `StringSeqOps1` is available as an implicit value. Alternatively, we can define `longestStrings`
165
-
as a member of a normal object. But then the method has to be brought into scope to be usable as an extension method.
166
-
167
-
```scala
168
-
objectStringSeqOps2{
169
-
deflongestStrings(thisxs: Seq[String]) = {
170
-
valmaxLength= xs.map(_.length).max
171
-
xs.filter(_.length == maxLength)
172
-
}
173
-
}
174
-
importStringSeqOps2.longestStrings
175
-
List("here", "is", "a", "list").longestStrings
176
-
```
177
-
The precise rules for resolving a selection to an extension method are as follows.
178
-
179
-
Assume a selection `e.m[Ts]` where `m` is not a member of `e`, where the type arguments `[Ts]` are optional,
180
-
and where `T` is the expected type. The following two rewritings are tried in order:
181
-
182
-
1. The selection is rewritten to `m[Ts](e)`.
183
-
2. If the first rewriting does not typecheck with expected type `T`, and there is an implicit value `i`
184
-
in either the current scope or in the implicit scope of `T`, and `i` defines an extension
185
-
method named `m`, then selection is expanded to `i.m[Ts](e)`.
186
-
This second rewriting is attempted at the time where the compiler also tries an implicit conversion
187
-
from `T` to a type containing `m`. If there is more than one way of rewriting, an ambiguity error results.
188
-
189
-
So `circle.circumference` translates to `CircleOps.circumference(circle)`, provided
190
-
`circle` has type `Circle` and `CircleOps` is an eligible implicit (i.e. it is visible at the point of call or it is defined in the companion object of `Circle`).
191
-
192
-
**Note**: The translation of extension methods is formulated on method calls. It is thus indepenent from the way infix operations are translated to method calls. For instamce,
193
-
if `+:` was formulated as an extension method, it would still have the `this` parameter come first, even though, seen as an operator, `+:` is right-binding:
194
-
```scala
195
-
def+: [T](thisxs: Seq[T))(x: T):Seq[T]
196
-
```
197
-
198
-
### Generic Extensions
199
-
200
-
The `StringSeqOps` examples extended a specific instance of a generic type. It is also possible
201
-
to extend a generic type by adding type parameters to an extension method:
As usual, type parameters of the extension method follow the defined method name. Nevertheless, such type parameters can already be used in the parameter clause that precedes the defined method name.
219
-
220
-
### A Larger Example
221
-
222
-
As a larger example, here is a way to define constructs for checking arbitrary postconditions using `ensuring` so that the checked result can be referred to simply by `result`. The example combines opaque aliases, implicit function types, and extensions to provide a zero-overhead abstraction.
**Explanations**: We use an implicit function type `implicit WrappedResult[T] => Boolean`
250
-
as the type of the condition of `ensuring`. An argument condition to `ensuring` such as
251
-
`(result == 6)` will therefore have an implicit value of type `WrappedResult[T]` in scope
252
-
to pass along to the `result` method. `WrappedResult` is a fresh type, to make sure that we do not get unwanted implicits in scope (this is good practice in all cases where implicit parameters are involved). Since `WrappedResult` is an opaque type alias, its values need not be boxed, and since `ensuring` is added as an extension method, its argument does not need boxing either. Hence, the implementation of `ensuring` is as about as efficient as the best possible code one could write by hand:
253
-
254
-
{ val result = List(1, 2, 3).sum
255
-
assert(result == 6)
256
-
result
257
-
}
258
-
259
-
### Rules for Overriding Extension Methods
260
-
261
-
Extension methods may override only extension methods and can be overridden only by extension methods.
262
-
263
-
### Extension Methods and TypeClasses
264
-
265
-
The rules for expanding extension methods make sure that they work seamlessly with typeclasses. For instance, consider `SemiGroup` and `Monoid`.
The `ListOrd` class is generic - it works for any type argument `T` that is itself an instance of `Ord`. In current Scala, we could not define `ListOrd` as an implicit class since implicit classes can only define implicit converions that take exactly one non-implicit value parameter. We propose to drop this requirement and to also allow implicit classes without any value parameters, or with only implicit value parameters. The generated implicit method would in each case follow the signature of the class. That is, for `ListOrd` we'd generate the method:
The required syntax extension just adds one clause for extension methods relative
361
-
to the [current syntax](https://github.com/lampepfl/dotty/blob/master/docs/docs/internals/syntax.md).
362
-
```
363
-
DefSig ::= id [DefTypeParamClause] [ExtParamClause] DefParamClauses
364
-
ExtParamClause ::= [nl] ‘(’ ‘this’ DefParam ‘)’
365
-
```
366
-
367
-
38
+
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.
368
39
40
+
The next sections elaborate such an alternative design. It consists of two proposals which are independent of each other:
369
41
42
+
- A proposal to replace implicit _definitions_ by [witness definitions](./witnesses.html)
43
+
.
44
+
- A proposal for a [new syntax](./witness-params.html) of implicit _parameters_ and their _arguments_.
0 commit comments