On iterating and decoding sum types in Elm

Written by kaisersly | Published 2017/05/03
Tech Story Tags: elm

TLDRvia the TL;DR App

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 FruitnextFruit 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 FruitfruitList =letbuildFruitList : Maybe Fruit -> List Fruit -> List FruitbuildFruitList maybeCurrentFruit allFruits =case maybeCurrentFruit ofNothing ->allFruitsJust currentFruit ->buildFruitList (nextFruit maybeCurrentFruit) (currentFruit :: allFruits)inbuildFruitList (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 FruitnextFruit 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 FruitfruitFromString 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 aenumList nextEnumFn =letbuildEnumList : Maybe a -> List a -> List abuildEnumList maybeCurrentEnum allEnums =case maybeCurrentEnum ofNothing ->allEnumsJust currentEnum ->buildEnumList (nextEnumFn maybeCurrentEnum) (currentEnum :: allEnums)inbuildEnumList (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!


Published by HackerNoon on 2017/05/03