|
| 1 | +--- |
| 2 | +layout: doc-page |
| 3 | +title: "Algebraic Data Types" |
| 4 | +nightlyOf: https://docs.scala-lang.org/scala3/reference/enums/adts.html |
| 5 | +--- |
| 6 | + |
| 7 | +The [`enum` concept](./enums.md) is general enough to also support algebraic data types (ADTs) and their generalized version (GADTs). |
| 8 | +Here is an example how an `Option` type can be represented as an ADT: |
| 9 | + |
| 10 | +```scala |
| 11 | +enum Option[+T]: |
| 12 | + case Some(x: T) |
| 13 | + case None |
| 14 | +``` |
| 15 | + |
| 16 | +This example introduces an `Option` enum with a covariant type parameter `T` consisting of two cases, `Some` and `None`. |
| 17 | +`Some` is parameterized with a value parameter `x`. |
| 18 | +It is a shorthand for writing a case class that extends `Option`. |
| 19 | +Since `None` is not parameterized, it is treated as a normal enum value. |
| 20 | + |
| 21 | +The `extends` clauses that were omitted in the example above can also be given explicitly: |
| 22 | + |
| 23 | +```scala |
| 24 | +enum Option[+T]: |
| 25 | + case Some(x: T) extends Option[T] |
| 26 | + case None extends Option[Nothing] |
| 27 | +``` |
| 28 | + |
| 29 | +Note that the parent type of the `None` value is inferred as `Option[Nothing]`. |
| 30 | +Generally, all covariant type parameters of the enum class are minimized in a compiler-generated `extends` clause whereas all contravariant type parameters are maximized. |
| 31 | +If `Option` was non-variant, you would need to give the extends clause of `None` explicitly. |
| 32 | + |
| 33 | +As for normal enum values, the cases of an `enum` are all defined in the `enum`'s companion object. |
| 34 | +So it's `Option.Some` and `Option.None` unless the definitions are "pulled out" with an import. |
| 35 | + |
| 36 | + |
| 37 | +## Widening of Constructor Application |
| 38 | + |
| 39 | +Observe here the inferred result types of the following expressions: |
| 40 | +```scala |
| 41 | +scala> Option.Some("hello") |
| 42 | +val res1: t2.Option[String] = Some(hello) |
| 43 | + |
| 44 | +scala> Option.None |
| 45 | +val res2: t2.Option[Nothing] = None |
| 46 | +``` |
| 47 | + |
| 48 | +Note that the type of the expressions above is always `Option`. |
| 49 | +Generally, the type of a enum case constructor application will be widened to the underlying enum type, unless a more specific type is expected. |
| 50 | +This is a subtle difference with respect to normal case classes. |
| 51 | +The classes making up the cases do exist, and can be unveiled, either by constructing them directly with a `new`, or by explicitly providing an expected type. |
| 52 | + |
| 53 | +```scala |
| 54 | +scala> new Option.Some(2) |
| 55 | +val res3: Option.Some[Int] = Some(2) |
| 56 | +scala> val x: Option.Some[Int] = Option.Some(3) |
| 57 | +val res4: Option.Some[Int] = Some(3) |
| 58 | +``` |
| 59 | + |
| 60 | +As all other enums, ADTs can define methods. |
| 61 | +For instance, here is `Option` again, with an `isDefined` method and an `Option(...)` constructor in its companion object. |
| 62 | + |
| 63 | +```scala |
| 64 | +enum Option[+T]: |
| 65 | + case Some(x: T) |
| 66 | + case None |
| 67 | + |
| 68 | + def isDefined: Boolean = this match |
| 69 | + case None => false |
| 70 | + case _ => true |
| 71 | + |
| 72 | +object Option: |
| 73 | + |
| 74 | + def apply[T >: Null](x: T): Option[T] = |
| 75 | + if x == null then None else Some(x) |
| 76 | + |
| 77 | +end Option |
| 78 | +``` |
| 79 | + |
| 80 | +Enumerations and ADTs have been presented as two different concepts. |
| 81 | +But since they share the same syntactic construct, they can be seen simply as two ends of a spectrum and it is perfectly possible to construct hybrids. |
| 82 | +For instance, the code below gives an implementation of `Color` either with three enum values or with a parameterized case that takes an RGB value. |
| 83 | + |
| 84 | +```scala |
| 85 | +enum Color(val rgb: Int): |
| 86 | + case Red extends Color(0xFF0000) |
| 87 | + case Green extends Color(0x00FF00) |
| 88 | + case Blue extends Color(0x0000FF) |
| 89 | + case Mix(mix: Int) extends Color(mix) |
| 90 | +``` |
0 commit comments