|
| 1 | +--- |
| 2 | +layout: doc-page |
| 3 | +title: "Opaque Type Aliases" |
| 4 | +--- |
| 5 | + |
| 6 | +Opaque types aliases provide type abstraction without any overhead. Example: |
| 7 | + |
| 8 | +```scala |
| 9 | +opaque type Logarithm = Double |
| 10 | +``` |
| 11 | + |
| 12 | +This introduces `Logarithm` as a new type, which is implemented as `Double` but is different from it. The fact that `Logarithm` is the same as `Double` is only known in the companion object of `Logarithm`. Here is a possible companion object: |
| 13 | + |
| 14 | +```scala |
| 15 | +object Logarithm { |
| 16 | + |
| 17 | + // These are the ways to lift to the logarithm type |
| 18 | + def apply(d: Double): Logarithm = math.log(d) |
| 19 | + |
| 20 | + def safe(d: Double): Option[Logarithm] = |
| 21 | + if (d > 0.0) Some(math.log(d)) else None |
| 22 | + |
| 23 | + // This is the first way to unlift the logarithm type |
| 24 | + def exponent(l: Logarithm): Double = l |
| 25 | + |
| 26 | + // Extension methods define opaque types' public APIs |
| 27 | + implicit class LogarithmOps(val `this`: Logarithm) extends AnyVal { |
| 28 | + // This is the second way to unlift the logarithm type |
| 29 | + def toDouble: Double = math.exp(`this`) |
| 30 | + def +(that: Logarithm): Logarithm = Logarithm(math.exp(`this`) + math.exp(that)) |
| 31 | + def *(that: Logarithm): Logarithm = Logarithm(`this` + that) |
| 32 | + } |
| 33 | +} |
| 34 | +``` |
| 35 | + |
| 36 | +The companion object contains with the `apply` and `safe` methods ways to convert from doubles to `Logarithm` values. It also adds an `exponent` function and a decorator that implements `+` and `*` on logarithm values, as well as a conversion `toDouble`. All this is possible because within object `Logarithm`, the type `Logarithm` is just an alias of `Double`. |
| 37 | + |
| 38 | +Outside the companion object, `Logarithm` is treated as a new abstract type. So the |
| 39 | +following operations would be valid because they use functionality implemented in the `Logarithm` object. |
| 40 | + |
| 41 | +```scala |
| 42 | + val l = Logarithm(1.0) |
| 43 | + val l3 = l * l2 |
| 44 | + val l4 = l + l2 |
| 45 | +``` |
| 46 | + |
| 47 | +But the following operations would lead to type errors: |
| 48 | + |
| 49 | +```scala |
| 50 | + val d: Double = l // error: found: Logarithm, required: Double |
| 51 | + val l2: Logarithm = 1.0 // error: found: Double, required: Logarithm |
| 52 | + l * 2 // error: found: Int(2), required: Logarithm |
| 53 | + l / l2 // error: `/` is not a member fo Logarithm |
| 54 | +``` |
| 55 | + |
| 56 | +For more details, see [Scala SIP 35](https://docs.scala-lang.org/sips/opaque-types.html). |
0 commit comments