|
| 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. |
0 commit comments