Skip to content

Commit cb7a9d8

Browse files
committed
Revert: Dotty docs for "this as modifier" scheme
(reverted from commit cf68e01)
1 parent abddd42 commit cb7a9d8

File tree

1 file changed

+67
-38
lines changed

1 file changed

+67
-38
lines changed

docs/docs/reference/extension-methods.md

Lines changed: 67 additions & 38 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 circumference(this c: Circle): Double = c.radius * math.Pi * 2
12+
def (c: Circle) circumference: Double = c.radius * math.Pi * 2
1313
}
1414
```
1515

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

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:
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:
2529
```scala
30+
def circumference(c: Circle): Double = c.radius * math.Pi * 2
31+
2632
assert(circle.circumference == CircleOps.circumference(circle))
2733
```
2834

@@ -35,7 +41,7 @@ When is an extension method considered? There are two possibilities. The first (
3541

3642
```scala
3743
implicit object StringSeqOps1 {
38-
def longestStrings(this xs: Seq[String]) = {
44+
def (xs: Seq[String]) longestStrings = {
3945
val maxLength = xs.map(_.length).max
4046
xs.filter(_.length == maxLength)
4147
}
@@ -49,8 +55,8 @@ is legal everywhere `StringSeqOps1` is available as an implicit value. Alternati
4955
as a member of a normal object. But then the method has to be brought into scope to be usable as an extension method.
5056

5157
```scala
52-
object StringSeqOps2{
53-
def longestStrings(this xs: Seq[String]) = {
58+
object StringOps2 {
59+
def (xs: Seq[String]) longestStrings = {
5460
val maxLength = xs.map(_.length).max
5561
xs.filter(_.length == maxLength)
5662
}
@@ -73,11 +79,26 @@ and where `T` is the expected type. The following two rewritings are tried in or
7379
So `circle.circumference` translates to `CircleOps.circumference(circle)`, provided
7480
`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`).
7581

76-
**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,
77-
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:
78-
```scala
79-
def +: [T](this xs: Seq[T])(x: T): Seq[T]
82+
### Operators
83+
84+
The extension method syntax also applies to the definition of operators.
85+
In each case the definition syntax mirrors the way the operator is applied.
86+
Examples:
87+
```
88+
def (x: String) < (y: String) = ...
89+
def (x: Elem) +: (xs: Seq[Elem]) = ...
90+
91+
"ab" + "c"
92+
1 +: List(2, 3)
93+
```
94+
The two definitions above translate to
95+
```
96+
def < (x: String)(y: String) = ...
97+
def +: (xs: Seq[Elem])(x: Elem) = ...
8098
```
99+
Note that swap of the two parameters `x` and `xs` when translating
100+
the right-binding operator `+:` to an extension method. This is analogous
101+
to the implementation of right binding operators as normal methods.
81102

82103
### Generic Extensions
83104

@@ -86,7 +107,7 @@ to extend a generic type by adding type parameters to an extension method:
86107

87108
```scala
88109
implicit object ListOps {
89-
def second[T](this xs: List[T]) = xs.tail.head
110+
def (xs: List[T]) second [T] = xs.tail.head
90111
}
91112
```
92113

@@ -95,11 +116,19 @@ or:
95116

96117
```scala
97118
implicit object ListListOps {
98-
def flattened[T](this xs: List[List[T]]) = xs.foldLeft[List[T]](Nil)(_ ++ _)
119+
def (xs: List[List[T]]) flattened [T] = xs.foldLeft[List[T]](Nil)(_ ++ _)
120+
}
121+
```
122+
123+
or:
124+
125+
```scala
126+
implicit object NumericOps {
127+
def (x: T) + [T : Numeric](y: T): T = implicitly[Numeric[T]].plus(x, y)
99128
}
100129
```
101130

102-
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.
131+
As usual, type parameters of the extension method follow the defined method name. Nevertheless, such type parameters can already be used in the preceding parameter clause.
103132

104133
### A Larger Example
105134

@@ -117,7 +146,7 @@ object PostConditions {
117146
def result[T](implicit er: WrappedResult[T]): T = WrappedResult.unwrap(er)
118147

119148
implicit object Ensuring {
120-
def ensuring[T](this x: T)(condition: implicit WrappedResult[T] => Boolean): T = {
149+
def (x: T) ensuring [T](condition: implicit WrappedResult[T] => Boolean): T = {
121150
implicit val wrapped = WrappedResult.wrap(x)
122151
assert(condition)
123152
x
@@ -140,25 +169,27 @@ to pass along to the `result` method. `WrappedResult` is a fresh type, to make s
140169
result
141170
}
142171

143-
### Rules for Overriding Extension Methods
144-
145-
Extension methods may override only extension methods and can be overridden only by extension methods.
172+
**A note on formatting** Having a parameter section in front of the defined
173+
method name makes it visually harder to discern what is being defined. To address
174+
that problem, it is recommended that the name of an extension method is
175+
preceded by a space and is also followed by a space if there are more parameters
176+
to come.
146177

147-
### Extension Methods and TypeClasses
178+
`### Extension Methods and TypeClasses
148179

149180
The rules for expanding extension methods make sure that they work seamlessly with typeclasses. For instance, consider `SemiGroup` and `Monoid`.
150181
```scala
151182
// Two typeclasses:
152183
trait SemiGroup[T] {
153-
def combine(this x: T)(y: T): T
184+
def (x: T) combine(y: T): T
154185
}
155186
trait Monoid[T] extends SemiGroup[T] {
156187
def unit: T
157188
}
158189

159190
// An instance declaration:
160191
implicit object StringMonoid extends Monoid[String] {
161-
def combine(this x: String)(y: String): String = x.concat(y)
192+
def (x: String) combine (y: String): String = x.concat(y)
162193
def unit: String = ""
163194
}
164195

@@ -175,21 +206,21 @@ extension methods apply everywhere their enclosing object is available as an imp
175206

176207
As another example, consider implementations of an `Ord` type class with a `minimum` value:
177208
```scala
178-
trait Ord[T] {
179-
def compareTo(this x: T)(y: T): Int
180-
def < (this x: T)(y: T) = x.compareTo(y) < 0
181-
def > (this x: T)(y: T) = x.compareTo(y) > 0
209+
trait Ord[T]
210+
def (x: T) compareTo (y: T): Int
211+
def (x: T) < (y: T) = x.compareTo(y) < 0
212+
def (x: T) > (y: T) = x.compareTo(y) > 0
182213
val minimum: T
183214
}
184215

185216
implicit object IntOrd extends Ord[Int] {
186-
def compareTo(this x: Int)(y: Int) =
217+
def (x: Int) compareTo (y: Int) =
187218
if (x < y) -1 else if (x > y) +1 else 0
188219
val minimum = Int.MinValue
189220
}
190221

191-
implicit class ListOrd[T: Ord] extends Ord[List[T]] {
192-
def compareTo(this xs: List[T])(ys: List[T]): Int = (xs, ys) match {
222+
class ListOrd[T: Ord] extends Ord[List[T]] {
223+
def (xs: List[T]) compareTo (ys: List[T]): Int = (xs, ys) match
193224
case (Nil, Nil) => 0
194225
case (Nil, _) => -1
195226
case (_, Nil) => +1
@@ -199,41 +230,39 @@ As another example, consider implementations of an `Ord` type class with a `mini
199230
}
200231
val minimum: List[T] = Nil
201232
}
233+
implicit def ListOrd[T: Ord]: ListOrd[T] = new ListOrd[T]
234+
202235

203236
def max[T: Ord](x: T, y: T): T = if (x < y) y else x
204237

205238
def max[T: Ord](xs: List[T]): T = (implicitly[Ord[T]].minimum /: xs)(max(_, _))
206239
```
207-
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:
208-
```scala
209-
implicit def ListOrd[T: Ord]: ListOrd[T] = new ListOrd[T]
210-
```
211240

212241
### Higher Kinds
213242

214243
Extension methods generalize to higher-kinded types without requiring special provisions. Example:
215244

216245
```scala
217246
trait Functor[F[_]] {
218-
def map[A, B](this x: F[A])(f: A => B): F[B]
247+
def (x: F[A]) map [A, B](f: A => B): F[B]
219248
}
220249

221250
trait Monad[F[_]] extends Functor[F] {
222-
def flatMap[A, B](this x: F[A])(f: A => F[B]): F[B]
223-
def map[A, B](this x: F[A])(f: A => B) = x.flatMap(f `andThen` pure)
251+
def (x: F[A]) flatMap [A, B](f: A => F[B]): F[B]
252+
def (x: F[A]) map [A, B](f: A => B) = x.flatMap(f `andThen` pure)
224253

225254
def pure[A](x: A): F[A]
226255
}
227256

228257
implicit object ListMonad extends Monad[List] {
229-
def flatMap[A, B](this xs: List[A])(f: A => List[B]): List[B] =
258+
def (xs: List[A]) flatMap [A, B](f: A => List[B]): List[B] =
230259
xs.flatMap(f)
231260
def pure[A](x: A): List[A] =
232261
List(x)
233262
}
234263

235264
implicit class ReaderMonad[Ctx] extends Monad[[X] => Ctx => X] {
236-
def flatMap[A, B](this r: Ctx => A)(f: A => Ctx => B): Ctx => B =
265+
def (r: Ctx => A) flatMap [A, B](f: A => Ctx => B): Ctx => B =
237266
ctx => f(r(ctx))(ctx)
238267
def pure[A](x: A): Ctx => A =
239268
ctx => x
@@ -244,8 +273,8 @@ Extension methods generalize to higher-kinded types without requiring special pr
244273
The required syntax extension just adds one clause for extension methods relative
245274
to the [current syntax](https://github.com/lampepfl/dotty/blob/master/docs/docs/internals/syntax.md).
246275
```
247-
DefSig ::= id [DefTypeParamClause] [ExtParamClause] DefParamClauses
248-
ExtParamClause ::= [nl] ‘(’ ‘this’ DefParam ‘)’
276+
DefSig ::= ...
277+
| ‘(’ DefParam ‘)’ id [DefTypeParamClause] DefParamClauses
249278
```
250279

251280

0 commit comments

Comments
 (0)