Union types
In Elm, Union Types are used for many things as they are incredibly flexible. They are also called ADT (Algebraic Data Types) in other languages. A simple union type looks like:
type Answer = Yes | No
The Answer
type can be either Yes
or No
.
Type and constructors
A union type has the following components:
type State = Pending | Done | Failed
^----^ ^---------------------^
type constructors
In this example State
is the type
. And Pending, Done and Failed
are constructors. These are called constructors because you construct a new instance of this type using them. e.g.
pendingState = Pending
Example
For example a function that has this signature:
respond : Answer -> String
Can either take Yes
or No
as the first argument e.g. respond Yes
is a valid call.
respond : Answer -> String
respond answer =
case answer of
Yes ->
...
No ->
...
Payload
Union types can have associated information with them:
type Answer = Yes | No | Other String
In this case, the tag Other
will have an associated string. You could call respond
like this:
respond (Other "Hello")
You need the parenthesis otherwise Elm will interpret this as passing two arguments to respond.
Used as functions
Note how we add a payload to Other
:
Other "Hello"
This is just like a function call where Other
is the function. Union types behave just like functions. For example given a type:
type Answer = Message Int String
You will create a Message
instance by:
Message 1 "Hello"
You can do partial application just like any other function.
Nesting
It is very common to 'nest' one union type in another.
type OtherAnswer = DontKnow | Perhaps | Undecided
type Answer = Yes | No | Other OtherAnswer
Then you can pass this to our respond
function (which expects an Answer
) like this:
respond (Other Perhaps)
Type variables
It is also possible to use type variables or stand-ins:
type Answer a = Yes | No | Other a
This is an Answer
that can be used with different types, e.g. Int, String.
For example, respond could look like this:
respond : Answer Int -> String
respond answer =
...
Here we are saying that the a
stand-in should be of type Int
by using the Answer Int
signature.
So later we will be able to call respond with:
respond (Other 123)
But respond (Other "Hello")
would fail because respond
expects an integer in place of a
.
A common use
One typical use of union types is passing around values in our program where the value can be one of a known set of possible values.
For example, in a typical web application, we can trigger messages to perform actions, e.g. load users, add user, delete user, etc. Some of these messages would have a payload.
It is common to use union types for this:
type Msg
= LoadUsers
| AddUser
| EditUser UserId
...
Some common union types
There are some common union types in Elm that you will see very often.
type Bool = True | False
There is no boolean in Elm, it is just a union type.
type Maybe a
= Nothing
| Just a
Maybe
represents the possibility of having nothing or something.
type Result error value
= Ok value
| Err error
Result
represents the possibility of having two outcomes from an operation. Ok
with the associated value and Err
with the associated error.
There is a lot more about Union types. If interested read more about this here.