On iterating and decoding sum types in Elm
EXPERIMENTAL
Thanks to imright_anduknowit and Enumerable_any on Reddit for their suggestions.
In Elm you don’t have a native way to list all tags in a sum type.
Let’s say we have a Fruit type defined as :
type Fruit
= Orange
| Lemon
| Apple
How can you create a list of all fruits ([Orange, Lemon, Fruit]) without missing one ?
First, let’s create a function nextFruit which given a Fruit will return a Maybe Fruit which represent the next fruit in the sum :
nextFruit : Maybe Fruit -> Maybe Fruit
nextFruit maybeCurrentFruit =
case maybeCurrentFruit of
Nothing ->
Just Orange
Just currentFruit ->
case currentFruit of
Orange ->
Just Lemon
Lemon ->
Just Apple
Apple ->
Nothing
Next, let’s create our Fruit list :
fruitList : List Fruit
fruitList =
let
buildFruitList : Maybe Fruit -> List Fruit -> List Fruit
buildFruitList maybeCurrentFruit allFruits =
case maybeCurrentFruit of
Nothing ->
allFruits
Just currentFruit ->
buildFruitList (nextFruit maybeCurrentFruit) (currentFruit :: allFruits)
in
buildFruitList (nextFruit Nothing) []
If you run fruitList you will get [Apple, Lemon, Orange].
Now, if you add Banana in Fruit, you will get a compiler error saying that the case in nextFruit doesn’t handle the Banana possibility. You’ll have to rewrite it :
nextFruit : Maybe Fruit -> Maybe Fruit
nextFruit maybeCurrentFruit =
case maybeCurrentFruit of
Nothing ->
Just Orange
Just currentFruit ->
case currentFruit of
Orange ->
Just Lemon
Lemon ->
Just Apple
Apple ->
Just Banana
Banana ->
Nothing
Ok, let’s be honest : you can make errors while writing the nextFruit function but 1/, you will be warned that a new tag has been added to the type and 2/, you can generate this code from the type declaration.
The final touch will be writing a function that given a String will return a Maybe Fruit :
fruitFromString : String -> Maybe Fruit
fruitFromString fruitString =
fruitList
|> List.filter (\x -> toString x == fruitString)
|> List.head
fruitFromString “Apple” will return Just Apple, fruitFromString “Pineapple” will return Nothing.
You can generalize fruitList :
enumList : (Maybe a -> Maybe a) -> List a
enumList nextEnumFn =
let
buildEnumList : Maybe a -> List a -> List a
buildEnumList maybeCurrentEnum allEnums =
case maybeCurrentEnum of
Nothing ->
allEnums
Just currentEnum ->
buildEnumList (nextEnumFn maybeCurrentEnum) (currentEnum :: allEnums)
in
buildEnumList (nextEnumFn Nothing) []
Hacker Noon is how hackers start their afternoons. We’re a part of the @AMIfamily. We are now accepting submissions and happy to discuss advertising & sponsorship opportunities.
To learn more, read our about page, like/message us on Facebook, or simply, tweet/DM @HackerNoon.
If you enjoyed this story, we recommend reading our latest tech stories and trending tech stories. Until next time, don’t take the realities of the world for granted!