When determining the type of union, you indicate all methods of constructing the value of this type. In its simplest form, this definition is as follows:
type Visibility = All | Active | Completed
As you might guess, this declares a Visibility type and defines three values, all of a Visibility type. The only way to build a value of type Visibility is to use one of these three options. Because of this, we often call them "constructors."
Here is a slightly more complex definition of the type of connection:
type TrainStatus = OnTime | Delayed Int
As you would expect, this defines two new “constructors,” OnTime and Delayed . But look at their types:
OnTime : TrainStatus Delayed : Int -> TrainStatus
The OnTime constructor takes null arguments, and this is just a value; it is already equal to TrainStatus . But Delayed declared as a constructor with one argument: it is a function that creates a new TrainStatus from Int . Thus, Delayed 5 , Delayed 10 and Delayed 100 are valid TrainStatus values. (We can interpret them as “5 minutes delayed” or something like that.)
The constructor may take several arguments; for example, if we want to include the reason for the delay as String:
type TrainStatus = OnTime | Delayed Int String ts : TrainStatus ts = Delayed 20 "The conductor took a short nap."
which defines Delayed : Int -> String -> TrainStatus .
If you are given TrainStatus , you can extract Int and String from it using pattern matching:
case ts of OnTime -> "Your train is on time!" Delayed minutes reason -> "Your train has been delayed by " ++ toString minutes ++ " because " ++ reason