There are times when you want to enumerate a union type. A common example is to render the <option> elements within a <select>. In general each disjoint component of the union may have varying data associated with it.
Let's start with a simple Union type used effectively like an enum of other languages:
type CarMake = AlfaRomeo | BMW | Ferarri | Ford | Honda | McLaren | Mercedes | Renault
Commonly a solution to having this accessible for enumeration is to manually define a List:
carMakes = [ AlfaRomeo, BMW, Ferarri, Ford, Honda, McLaren, Mercedes, Renault ]
The problem of course with this is that we have two parallel definitions that must be manually kept in sync which is exactly the sort of thing computers, compilers and type systems specifically should solve.
Note that we did add some information rather than merely performing double entry. The
carMakes list defines an ordering whereas the
CarMake type does not. The Elm language and compiler could infer this from the type definition and provide an enumeration mechanism but that's not part of the language.
So let's get on with it and see what's possible. Elm does ensure exhaustive case matching of Union types.
noop : CarMake -> CarMakenoop x =case x ofAlfaRomeo -> xBMW -> x...Renault -> x
Now if in the above code one of the component types was missed, the compiler would issue an error. This by itself doesn't solve our need. The main problem is that each case is processed independently and we don't have a way of processing every path to produce the full set of values. Even if we fed it our manual list of
carMakes it would only be able to produce the same set we put in replicating any accidental omission.
What we can do is what an accountant would do in such a situation, double-entry and cross check. We can create a
successor : Maybe CarMake -> Maybe CarMakesuccessor m =case m ofNothing -> AlfaRomeoSome c ->case c ofAlfaRomeo -> BMWBMW -> Ferarri...Mercedes -> RenaultRenault -> Nothing
To produce a list from the
successor definition we can start with
Nothing and produce each
CarMake until we end with
carMakes2 = ... [Work in progress]
All that's left now is to assert
carMakes is the same as
We can do one better by writing this check once rather than for each Union type.
checkUnionSuccessor : List X -> Maybe X -> Maybe X -> BoolcheckUnionSuccessor xs successor =... [Work in progress]