A simple union type allows us to define an enumeration, like this
type Direction = North | East | South | West
In the above example we've defined the type Direction
, but we've also
created and defined the values North
, East
, South
and West
.
If we put this code into
UnionTypes.elm
then we can use them like this in the REPL:
> import UnionTypes exposing (..)
> East
East : Direction
>
You can see that East
is a value, and it has type Direction
.
Here's an example of using this
describe : Direction -> String
describe dir =
case dir of
North -> "Cold"
East -> "Arid"
South -> "Hot"
West -> "Humid"
Note that the code above won't compile if we miss out one of the directions, because the compiler expects an exhaustive match.
Union types also allow us to use more complex constructors.
The Character
type defined next is a simple enumeration, but
the Card
type is a mix of tagged Value
and tagged String
types.
type Character = Ace | King | Queen | Jack
type Value = Pips Int | Name Character
type Card =
Heart Value
| Diamond Value
| Club Value
| Spade Value
| Joker String
d10 = Diamond (Pips 10)
hking = Heart (Name King)
joker1 = Joker "Laughing Jeremy"
Let's look at the values' types:
> import UnionTypes exposing (..)
> d10
Diamond (Pips 10) : Card
> hking
Heart (Name King) : Card
> joker1
Joker ("Laughing Jeremy") : Card
>
For a union type to be valid, all the types in the union must be creating a new type. So we can't do this, for example:
type Counter = Int | { name : String, c : Int}
But we can do this if AnonCounter
and NamedCounter
are entirely
new and haven't been defined already:
type Counter = AnonCounter Int | NamedCounter { name : String, c : Int }
Both AnonCounter
and NamedCounter
act as tags for the types that follow.