Skip to content

Commit cf68e01

Browse files
committed
Dotty docs for "this as modifier" scheme
1 parent dc36648 commit cf68e01

File tree

1 file changed

+25
-61
lines changed

1 file changed

+25
-61
lines changed

docs/docs/reference/extend/extension-methods.md

Lines changed: 25 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ Extension methods allow one to add methods to a type after the type is defined.
99
case class Circle(x: Double, y: Double, radius: Double)
1010

1111
implicit object CircleOps {
12-
def (c: Circle).circumference: Double = c.radius * math.Pi * 2
12+
def circumference(this c: Circle): Double = c.radius * math.Pi * 2
1313
}
1414
```
1515

@@ -20,15 +20,9 @@ implicit object CircleOps {
2020
circle.circumference
2121
```
2222

23-
### Translation of Extension Methods
24-
25-
Extension methods are methods that have a parameter clause in front of the defined
26-
identifier. They translate to methods where the leading parameter section is moved
27-
to after the defined identifier. So, the definition of `circumference` above translates
28-
to the plain method, and can also be invoked as such:
23+
Extension methods are methods that have a `this` modifier for the first parameter.
24+
They can also be invoked as plain methods. So the following holds:
2925
```scala
30-
def circumference(c: Circle): Double = c.radius * math.Pi * 2
31-
3226
assert(circle.circumference == CircleOps.circumference(circle))
3327
```
3428

@@ -50,7 +44,7 @@ object adds a `longestStrings` extension method to a `Seq[String]`:
5044

5145
```scala
5246
implicit object StringOps {
53-
def (xs: Seq[String]).longestStrings = {
47+
def longestStrings(this xs: Seq[String]) = {
5448
val maxLength = xs.map(_.length).max
5549
xs.filter(_.length == maxLength)
5650
}
@@ -64,7 +58,7 @@ to extend a generic type by adding type parameters to an extension method:
6458

6559
```scala
6660
implicit object ListOps {
67-
def [T](xs: List[T]).second = xs.tail.head
61+
def second[T](this xs: List[T]) = xs.tail.head
6862
}
6963
```
7064

@@ -73,12 +67,11 @@ or:
7367

7468
```scala
7569
implicit object ListListOps {
76-
def [T](xs: List[List[T]]).flattened = xs.foldLeft[List[T]](Nil)(_ ++ _)
70+
def flattened[T](this xs: List[List[T]]) = xs.foldLeft[List[T]](Nil)(_ ++ _)
7771
}
7872
```
7973

80-
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
81-
the defined method name.
74+
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.
8275

8376
### A Larger Example
8477

@@ -96,7 +89,7 @@ object PostConditions {
9689
def result[T](implicit er: WrappedResult[T]): T = WrappedResult.unwrap(er)
9790

9891
implicit object Ensuring {
99-
def [T](x: T).ensuring(condition: implicit WrappedResult[T] => Boolean): T = {
92+
def ensuring[T](this x: T)(condition: implicit WrappedResult[T] => Boolean): T = {
10093
implicit val wrapped = WrappedResult.wrap(x)
10194
assert(condition)
10295
x
@@ -119,50 +112,25 @@ to pass along to the `result` method. `WrappedResult` is a fresh type, to make s
119112
result
120113
}
121114

122-
### Extension Operators
123-
124-
The `.` between leading parameter section and defined name in an extension method is optional. If the extension method is an operator, leaving out the dot leads to clearer
125-
syntax that resembles the intended usage pattern:
126-
127-
```scala
128-
implicit object NumericOps {
129-
def [T : Numeric](x: T) + (y: T): T = implicitly[Numeric[T]].plus(x, y)
130-
}
131-
```
132-
133-
An infix operation `x op y` of an extension method `op` coming from `z` is always translated to `z.op(x)(y)`, irrespective of whether `op` is right-associative or not. So, no implicit swapping of arguments takes place for extension methods ending in a `:`. For instance,
134-
here is the "domino"-operator brought back as an extension method:
115+
### Rules for Overriding Extension Methods
135116

136-
```scala
137-
implicit object SeqOps {
138-
def [A](x: A) /: [B](xs: Seq[B])(op: (A, B) => A): A =
139-
xs.foldLeft(x)(op)
140-
}
141-
```
142-
A call like
143-
```scala
144-
(0 /: List(1, 2, 3)) (_ + _)
145-
```
146-
is translated to
147-
```scala
148-
SeqOps./: (0) (List(1, 2, 3)) (_ + _)
149-
```
117+
Extension methods may override only extension methods and can be overridden only by extension methods.
150118

151119
### Extension Methods and TypeClasses
152120

153121
The rules for expanding extension methods make sure that they work seamlessly with typeclasses. For instance, consider `SemiGroup` and `Monoid`.
154122
```scala
155123
// Two typeclasses:
156124
trait SemiGroup[T] {
157-
def (x: T).combine(y: T): T
125+
def combine(this x: T)(y: T): T
158126
}
159127
trait Monoid[T] extends SemiGroup[T] {
160128
def unit: T
161129
}
162130

163131
// An instance declaration:
164132
implicit object StringMonoid extends Monoid[String] {
165-
def (x: String).combine(y: String): String = x.concat(y)
133+
def combine(this x: String)(y: String): String = x.concat(y)
166134
def unit: String = ""
167135
}
168136

@@ -180,20 +148,20 @@ extension methods apply everywhere their enclosing object is available as an imp
180148
As another example, consider implementations of an `Ord` type class with a `minimum` value:
181149
```scala
182150
trait Ord[T]
183-
def (x: T).compareTo(y: T): Int
184-
def (x: T) < (that: T) = x.compareTo(y) < 0
185-
def (x: T) > (that: T) = x.compareTo(y) > 0
151+
def compareTo(this x: T)(y: T): Int
152+
def < (this x: T)(y: T) = x.compareTo(y) < 0
153+
def > (this x: T)(y: T) = x.compareTo(y) > 0
186154
val minimum: T
187155
}
188156

189157
implicit object IntOrd extends Ord[Int] {
190-
def (x: Int).compareTo(y: Int) =
158+
def compareTo(this x: Int)(y: Int) =
191159
if (x < y) -1 else if (x > y) +1 else 0
192160
val minimum = Int.MinValue
193161
}
194162

195163
implicit class ListOrd[T: Ord] extends Ord[List[T]] {
196-
def (xs: List[T]).compareTo(ys: List[T]): Int = (xs, ys) match
164+
def compareTo(this xs: List[T])(ys: List[T]): Int = (xs, ys) match
197165
case (Nil, Nil) => 0
198166
case (Nil, _) => -1
199167
case (_, Nil) => +1
@@ -207,10 +175,7 @@ As another example, consider implementations of an `Ord` type class with a `mini
207175

208176
def max[T: Ord](xs: List[T]): T = (implicitly[Ord[T]].minimum /: xs)(max(_, _))
209177
```
210-
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
211-
implicit classes can only define implicit converions that take exactly one non-implicit
212-
value parameter. We propose to drop this requirement and to also allow implicit classes
213-
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:
178+
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:
214179
```scala
215180
implicit def ListOrd[T: Ord]: ListOrd[T] = new ListOrd[T]
216181
```
@@ -221,25 +186,25 @@ Extension methods generalize to higher-kinded types without requiring special pr
221186

222187
```scala
223188
trait Functor[F[_]] {
224-
def [A](x: F[A]).map[B](f: A => B): F[B]
189+
def map[A, B](this x: F[A])(f: A => B): F[B]
225190
}
226191

227192
trait Monad[F[_]] extends Functor[F] {
228-
def [A](x: F[A]).flatMap[B](f: A => F[B]): F[B]
229-
def [A](x: F[A]).map[B](f: A => B) = x.flatMap(f `andThen` pure)
193+
def flatMap[A, B](this x: F[A])(f: A => F[B]): F[B]
194+
def map[A, B](this x: F[A])(f: A => B) = x.flatMap(f `andThen` pure)
230195

231196
def pure[A](x: A): F[A]
232197
}
233198

234199
implicit object ListMonad extends Monad[List] {
235-
def [A](xs: List[A]).flatMap[B](f: A => List[B]): List[B] =
200+
def flatMap[A, B](this xs: List[A])(f: A => List[B]): List[B] =
236201
xs.flatMap(f)
237202
def pure[A](x: A): List[A] =
238203
List(x)
239204
}
240205

241206
implicit class ReaderMonad[Ctx] extends Monad[[X] => Ctx => X] {
242-
def [A](r: Ctx => A).flatMap[B](f: A => Ctx => B): Ctx => B =
207+
def flatMap[A, B](this r: Ctx => A)(f: A => Ctx => B): Ctx => B =
243208
ctx => f(r(ctx))(ctx)
244209
def pure[A](x: A): Ctx => A =
245210
ctx => x
@@ -250,9 +215,8 @@ Extension methods generalize to higher-kinded types without requiring special pr
250215
The required syntax extension just adds one clause for extension methods relative
251216
to the [current syntax](https://github.com/lampepfl/dotty/blob/master/docs/docs/internals/syntax.md).
252217
```
253-
DefSig ::= ...
254-
| [DefTypeParamClause] ‘(’ DefParam ‘)’ [‘.’]
255-
id [DefTypeParamClause] DefParamClauses
218+
DefSig ::= id [DefTypeParamClause] [ExtParamClause] DefParamClauses
219+
ExtParamClause ::= [nl] ‘(’ ‘this’ DefParam ‘)’
256220
```
257221

258222

0 commit comments

Comments
 (0)