# Algebraic Data Types

The `enum` concept is general enough to also support algebraic data types (ADTs) and their generalized version (GADTs). Here is an example how an `Option` type can be represented as an ADT:

``````enum Option[+T]:
case Some(x: T)
case None
``````

This example introduces an `Option` enum with a covariant type parameter `T` consisting of two cases, `Some` and `None`. `Some` is parameterized with a value parameter `x`. It is a shorthand for writing a case class that extends `Option`. Since `None` is not parameterized, it is treated as a normal enum value.

The `extends` clauses that were omitted in the example above can also be given explicitly:

``````enum Option[+T]:
case Some(x: T) extends Option[T]
case None       extends Option[Nothing]
``````

Note that the parent type of the `None` value is inferred as `Option[Nothing]`. Generally, all covariant type parameters of the enum class are minimized in a compiler-generated `extends` clause whereas all contravariant type parameters are maximized. If `Option` was non-variant, you would need to give the extends clause of `None` explicitly.

As for normal enum values, the cases of an `enum` are all defined in the `enum`s companion object. So it's `Option.Some` and `Option.None` unless the definitions are "pulled out" with an import:

``````scala> Option.Some("hello")
val res1: t2.Option[String] = Some(hello)

scala> Option.None
val res2: t2.Option[Nothing] = None
``````

Note that the type of the expressions above is always `Option`. Generally, the type of a enum case constructor application will be widened to the underlying enum type, unless a more specific type is expected. This is a subtle difference with respect to normal case classes. 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.

``````scala> new Option.Some(2)
val res3: Option.Some[Int] = Some(2)
scala> val x: Option.Some[Int] = Option.Some(3)
val res4: Option.Some[Int] = Some(3)
``````

As all other enums, ADTs can define methods. For instance, here is `Option` again, with an `isDefined` method and an `Option(...)` constructor in its companion object.

``````enum Option[+T]:
case Some(x: T)
case None

def isDefined: Boolean = this match
case None => false
case _    => true

object Option:

def apply[T >: Null](x: T): Option[T] =
if x == null then None else Some(x)

end Option
``````

Enumerations and ADTs have been presented as two different concepts. 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. 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.

``````enum Color(val rgb: Int):
case Red   extends Color(0xFF0000)
case Green extends Color(0x00FF00)
case Blue  extends Color(0x0000FF)
case Mix(mix: Int) extends Color(mix)
``````

### Parameter Variance of Enums

By default, parameterized cases of enums with type parameters will copy the type parameters of their parent, along with any variance notations. As usual, it is important to use type parameters carefully when they are variant, as shown below:

The following `View` enum has a contravariant type parameter `T` and a single case `Refl`, representing a function mapping a type `T` to itself:

``````enum View[-T]:
case Refl(f: T => T)
``````

The definition of `Refl` is incorrect, as it uses contravariant type `T` in the covariant result position of a function type, leading to the following error:

``````-- Error: View.scala:2:12 --------
2 |   case Refl(f: T => T)
|             ^^^^^^^^^
|contravariant type T occurs in covariant position in type T => T of value f
|enum case Refl requires explicit declaration of type T to resolve this issue.
``````

Because `Refl` does not declare explicit parameters, it looks to the compiler like the following:

``````enum View[-T]:
case Refl[/*synthetic*/-T1](f: T1 => T1) extends View[T1]
``````

The compiler has inferred for `Refl` the contravariant type parameter `T1`, following `T` in `View`. We can now clearly see that `Refl` needs to declare its own non-variant type parameter to correctly type `f`, and can remedy the error by the following change to `Refl`:

``````enum View[-T]:
-  case Refl(f: T => T)
+  case Refl[R](f: R => R) extends View[R]
``````

Above, type `R` is chosen as the parameter for `Refl` to highlight that it has a different meaning to type `T` in `View`, but any name will do.

After some further changes, a more complete implementation of `View` can be given as follows and be used as the function type `T => U`:

``````enum View[-T, +U] extends (T => U):
case Refl[R](f: R => R) extends View[R, R]

final def apply(t: T): U = this match
case refl: Refl[r] => refl.f(t)
``````

### Syntax of Enums

Changes to the syntax fall in two categories: enum definitions and cases inside enums. The changes are specified below as deltas with respect to the Scala syntax given here

1. Enum definitions are defined as follows:

``````TmplDef   ::=  `enum' EnumDef
EnumDef   ::=  id ClassConstr [`extends' [ConstrApps]] EnumBody
EnumBody  ::=  [nl] ‘{’ [SelfType] EnumStat {semi EnumStat} ‘}’
EnumStat  ::=  TemplateStat
|  {Annotation [nl]} {Modifier} EnumCase
``````
2. Cases of enums are defined as follows:

``````EnumCase  ::=  `case' (id ClassConstr [`extends' ConstrApps]] | ids)
``````

### Reference

For more information, see Issue #1970.