Skip to content

Commit 3664c88

Browse files
committed
More docs for augmentations
1 parent 24a1890 commit 3664c88

File tree

6 files changed

+209
-5
lines changed

6 files changed

+209
-5
lines changed

docs/docs/reference/augments/method-augments.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ augment List[type T] {
7878
```
7979

8080
The `type T` argument indicates that the augment applies to `List[T]`s for any type `T`.
81-
We also say that `type T` introduces `T` as a variable in the type pattern `List[type T]`.
81+
We also say that `type T` introduces `T` as a _variable_ in the type pattern `List[type T]`.
8282
Type variables may appear anywhere in a type pattern. Example:
8383

8484
```scala
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
---
2+
layout: doc-page
3+
title: "Trait Augmentations"
4+
---
5+
6+
In addition to adding methods, an augmentation can also implement traits and classes. For instance,
7+
8+
```scala
9+
trait HasArea {
10+
def area: Double
11+
}
12+
13+
augment circleOps @ Circle extends HasArea {
14+
def area = this.radius * this.radius * math.Pi
15+
}
16+
```
17+
18+
This augemntation makes `Circle` implement the `HasArea` trait. Specifically, it defines an implicit subclass of `HasArea`
19+
which takes a `Circle` as argument and provides the given implementation. Hence, the implementation of the augmentation above would be like this
20+
21+
```scala
22+
implicit class circleOps($this: Circle) extends HasArea {
23+
def area = $this.radius * $this.radius * math.Pi
24+
}
25+
```
26+
27+
A trait augmentation can thus provide a kind of "extends" relationship that can be defined independently of the types it connects.
28+
29+
### Generic Trait Augmentations
30+
31+
Just like method augmentations, trait augmentations can be generic with and their type parameters can have bounds.
32+
33+
For example, assume we have the following two traits, which define binary and unary (infix) equality tests:
34+
35+
```scala
36+
trait Eql[T] {
37+
def eql (x: T, y: T): Boolean
38+
}
39+
40+
trait HasEql[T] {
41+
def === (that: T)
42+
}
43+
```
44+
45+
The following augment makes any type `T` with an implicit `Eql[T]` instance implement `HasEql`:
46+
47+
```scala
48+
augment (type T: Eql) extends HasEql[T] {
49+
def === (that: T): Boolean = implicitly[Eql[T]].eql(this, that)
50+
}
51+
```
52+
53+
### Syntax of Augmentations
54+
55+
The syntax of augmentations iss pecified below as a delta with respect to the Scala syntax given [here](http://dotty.epfl.ch/docs/internals/syntax.html)
56+
57+
Augmentation ::= ‘augment’ [id ‘@’] BindingTypePattern
58+
[[nl] ImplicitParamClause] AugmentClause
59+
AugmentClause ::= ‘extends’ Template
60+
| [nl] ‘{’ ‘def’ DefDef {semi ‘def’ DefDef} ‘}’
61+
62+
ImplicitParamClause ::= [nl] ‘(’ ImplicitMods ClsParams ‘)’
63+
ImplicitMods ::= `implicit` [`ghost`] | `ghost` `implicit`
64+
65+
BindingTypePattern: := AnnotType
66+
Type ::= ...
67+
| ‘type’ TypeParamCore (if inside a BindingTypePattern)
68+
TypeParamCore ::= id [HkTypeParamClause] TypeParamBounds
69+
70+
In this definition, type patterns and types share the same productions. However, the production
71+
72+
Type ::= ‘type’ TypeParamCore
73+
74+
is applicable only inside a `BindingTypePattern`.
75+
76+
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
---
2+
layout: doc-page
3+
title: "Translation of Augmentations"
4+
---
5+
6+
Augmentations are closely related to implicit classes and can be translated into them. In short,
7+
a method augmentation translates into an implicit value class and a trait implementation translates
8+
into a regular implicit class. The following sections sketch this translation.
9+
10+
Conversely, it is conceivable (and desirable) to replace most usages of implicit classes and value classes by augmentations and [opaque types](../opaques.html). We plan to [drop](../dropped/implicit-value-classes.html)
11+
these constructs in future versions of the language. Once that is achieved, the translations described
12+
below can be simply composed with the existing translations of implicit and value classes into the core language. It is
13+
not necessary to retain implicit and value classes as an intermediate step.
14+
15+
16+
### Decomposing Type Patterns
17+
18+
First, define a function `decompose` to decompose a type pattern `TP` into a list of type binders `tparams` and a type `T`. Going from left to right, every type binder `type U <bounds>` in `TP` is added to `tparams` and is replaced by the reference `U` in `T`. For instance, the type pattern
19+
20+
```scala
21+
Map[type K <: AnyRef, List[type T]]
22+
```
23+
would be decomposed into the binders `type K <: AnyRef` and `type T` and the type `Map[K, List[T]]`.
24+
25+
### Translation of Method Augmentations
26+
27+
Now, a assume a method augmentation
28+
29+
augment <id> @ <TP> <params> { <defs> }
30+
31+
where `<id> @` or `<params>` can be absent. For simplicity assume that there are no context bounds in
32+
type definitions of `<TP>`. This is no essential restriction as any such context bounds can be rewritten in a prior step to be evidence paramseters in `<params>`. Let `(<tparams>, <T>)` be the decomposition of `<TP>`.
33+
The augment clause can be translated to the following implicit value class:
34+
35+
implicit class <id> <tparams> ($this: <T>) extends AnyVal { <defs'> }
36+
37+
Here `<defs'>` results from `<defs>` by augmenting any definition in <defs> with the parameters <params> and
38+
replacing any occurrence of `this` with `$this`. If the label `<id> @` is missing, a fresh compiler-generated name is chosen instead as the name of the implicit class.
39+
40+
For example, the augmentation
41+
42+
```scala
43+
augment seqOps @ Seq[type T: math.Ordering] {
44+
def indexOfLargest = this.zipWithIndex.maxBy(_._1)._2
45+
def indexOfSmallest = this.zipWithIndex.minBy(_._1)._2
46+
}
47+
```
48+
49+
would be translated to:
50+
51+
```scala
52+
implicit class seqOps[T]($this: List[T]) extends AnyVal {
53+
def indexOfLargest (implicit $ev: math.Ordering[T]) = $this.zipWithIndex.maxBy(_._1)._2
54+
def indexOfSmallest(implicit $ev: math.Ordering[T]) = $this.zipWithIndex.minBy(_._1)._2
55+
}
56+
```
57+
58+
### Translation of Trait Augmentations
59+
60+
Now, assume a trait augmentation
61+
62+
augment <id> @ <TP> <params> extends <parents> { <body> }
63+
64+
Let again `(<tparams>, <T>)` be the decomposition of `<TP>`. This augmentation is translated to
65+
66+
implicit class <id> <tparams> ($this: <T>) <params> extends <parents> { <body'> }
67+
68+
As before, `<body'>` results from `<body>` by replacing any occurrence of `this` with `$this`. However, all
69+
parameters in <params> now stay on the class definition, instead of beging distributed to all members in `<body>`. This is necessary in general, since `<body>` might contain value definitions or other statements that cannot be
70+
parameterized.
71+
72+
For example, the trait augmentation
73+
74+
```scala
75+
class Test {
76+
augment (type T: Eql) extends HasEql[T] {
77+
def === (that: T): Boolean = implicitly[Eql[T]].eql(this, that)
78+
}
79+
}
80+
```
81+
82+
would be translated to
83+
84+
```scala
85+
class Test {
86+
implicit class Test$$_augment_T_to_HasEql_1 [T]
87+
($this: T)(implicit $ev: Eql[T]) extends HasEql[T] {
88+
def === (that: T): Boolean = implicitly[Eql[T]].eql($this, that)
89+
}
90+
}
91+
```
92+
93+
where the name `Test$$_augment_T_to_HasEql_1` is compiler generated and implementation dependent.
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
---
2+
layout: doc-page
3+
title: Dropped: Implicit Classes and Value Classes
4+
---
5+
6+
Scala uses implicit classes to define extension methods and conversions. E.g., from `Predef.scala`:
7+
8+
```scala
9+
implicit final class ArrowAssoc[A](private val self: A) extends AnyVal {
10+
def -> [B](y: B): (A, B) = (self, y)
11+
}
12+
```
13+
14+
Implicit classes and value classes are still supported in Dotty, in order to support cross compilation with Scala 2. But they will be phased out in the future. The `ArrowAssoc` class can be written as an [augmentation](../augments/method-augments.html) as follows:
15+
16+
```scala
17+
augment (type A) {
18+
def -> [B](that: B): (A, B) = (this, that)
19+
}
20+
```
21+
22+
Most other uses of value classes can be expressed by [opaque type aliases](../opaque.html).
23+
There are only two aspects of value classes that are not covered:
24+
25+
- A user-definable `toString` method. E.g. if `Meter` is a value class implemented in terms of `Double`, it is possible to define `toString` to that `new Meter(10).toString` prints `10m` instead of `10`.
26+
27+
- Type tests and checked type casts for value classes. E.g. if `x: Any` once can have a pattern match like this:
28+
29+
x match { case m: Meter => ... }
30+
31+
The match succeeds if `x` is a `Meter` but not if it is `Double`. By contrast, an implementation in terms of
32+
opaque type aliases would flag the match with an unchecked warning and succeed for both. This is a consequence of using the same runtime representation for `Meter` and `Double`.
33+
34+
Given the quite high complexity of value classes, in terms of rules what is legal and what is not, and in terms of their boxing model, it is attractive to drop them. The combinaton of opaque types and augmentations is both simpler and generally more pleasant to use.
35+

docs/sidebar.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,6 @@ sidebar:
3535
subsection:
3636
- title: Method Augmentations
3737
url: docs/reference/augments/method-augments.html
38-
- title: Type Patterns
39-
url: docs/reference/augments/type-patterns.html
4038
- title: Trait Augmentations
4139
url: docs/reference/augments/trait-augments.html
4240
- title: Translation of Augmentations
@@ -113,6 +111,8 @@ sidebar:
113111
url: docs/reference/dropped/auto-apply.html
114112
- title: Weak Conformance
115113
url: docs/reference/dropped/weak-conformance.html
114+
- title: Implicit and Value Classes
115+
url: docs/reference/dropped/implicit-value-classes.html
116116
- title: Contributing
117117
subsection:
118118
- title: Getting Started

tests/run/augment.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,13 @@ object augments {
1919
def area = this.radius * this.radius * math.Pi
2020
}
2121

22-
// Generic trait implementations
22+
// Generic augmentations
2323

2424
augment List[type T] {
2525
def second = this.tail.head
2626
}
2727

28-
// Specific trait implementations
28+
// Specific augmentations
2929

3030
augment List[Int] {
3131
def maxx = (0 /: this)(_ `max` _)

0 commit comments

Comments
 (0)