Learning Elm Data Types

In this post I take a short meander through the Elm Dict module.

Dictionaries

Elm has a Dict type which stores key, value pairs. To store the simplest example I can think of we can use:

> a = Dict.insert 'a' 1 Dict.empty
Dict.fromList [('a',1)] : Dict.Dict Char number

The first line, is relatively simple to understand: it inserts a key, value pair into an empty Dict. It’s pretty verbose, and as a learner I find the ‘.fromList’ somewhat confusing. No doubt as I get more into the language this syntax style will start to feel more comfortable.

Next up, to update this Dict we can use the following:

> b = Dict.update 'a' (\_ -> Just 2) a
Dict.fromList [('a',2)] : Dict.Dict Char number

In the example above, we’re just updating the value of the original Dict, a, from 1 to 2. Elm expects the second argument of update to be a function which has a type signature of Maybe a-> Maybe a, where a is the type of the value in the key, value pair (we’re using integers).

The example above uses a function which always updates our Dict value to 2 — the underscore in the anonymous function means that the input parameter is ignored.

To do something more useful, we need to roll out a whole new named function. The function below accepts a Maybe Int. If the input is good it returns the same Maybe type with its contents multiplied by 3, otherwise if the value doesn’t exist it returns Just 0.

times3 : Maybe Int -> Maybe Int
times3 x =
case x of
Just num -> Just <| num * 3
Nothing -> Just 0
Update:
As an aside, an alternative implementation of function
times3 above, using function composition is: 
times3' = Maybe.withDefault 0 >> (*) 3 >> Just

Which we can test using:

> c = Dict.update 'a' times3 b
Dict.fromList [('a',6)] : Dict.Dict Char number

So far so good!

Lets iterate on this!

The Dict module also has some functions that iterate over all the key, value pairs in the supplied dictionary. Here’s we’ll take a quick look at a couple of cases.

Firstly, let’s start with some test data:

> a = Dict.fromList [('a', 1), ('b', 2), ('c', 3)]
Dict.fromList [('a',1),('b',2),('c',3)] : Dict.Dict Char number

According to the Elm docs, the Dict module exposes a map function with the following type signature:

map : (comparable -> a -> b) -> Dict comparable a -> Dict comparable b

For a time I scratched my head, wondering what a comparable was. It turns out it’s the dictionary key. In the examples we’ve been using so far the key has been a simple Char. The second and third arguments are both Dict’s containing key, value pairs — the values are expressed as a and b. This means would could map over a Dict containing values of one type and return another Dict containing values of a different type, say from Int to String, say. However, in our simple case both a and b are the same type, namely Int. Therefore, the map function’s first argument needs to be a function that takes a Char and Int, and returns an Int.

b = Dict.map (\key value -> value * 4) a
Dict.fromList [('a',4),('b',8),('c',12)] : Dict.Dict Char number

In the example above, we using an anonymous function that takes a key, value pair, and returns the value multiplied by 4. Note, we’re not using the key parameter, and to be honest I’m not sure why you’d want the key in a map expression. But, the mysteries of a new language reveal themselves gradually, so let’s move on.

To fold left or right? That is the question!

Just to get more familiar with Elm method type signatures, let’s look another example: foldl.

foldl : (comparable -> v -> b -> b) -> b -> Dict comparable v -> b

Firstly, we see some comparable’s again, which we’re now familiar with. v is our value from our key, value pairs, but what’s b?

Currently, the Elm docs don’t give any description of b, nor an example use case. (I miss the Elixir docs already). So I dredged from the depths of my memory what foldl (sometimes known as reduce) does in other languages.

Normally, foldl takes a function, an initial value, plus a collection of some sort. So if we wanted to implement sum using foldl, the arguments would be an addition function, 0 and a list of integers, say. Thus, I deduced that b is of type Int, in our case. Therefore, we need to write a function, for the first argument, that takes Char, Int, Int, and returns Int.

> c = Dict.foldl (\key x acc -> acc + x) 0 b
24 : number

So, this example iterates over the contents of our Dict, b, accumulating the values as it goes. As before, we’re not using the key; I look forward to seeing an example where this is used.

Well, that wraps it up for this time. I hope some of it made sense!

p.s. The online editor, try-elm is really useful for trying out larger chunks of Elm code. Here’s a snippet I prepared earlier.