23-Dec

Elm, Functional

# Mapping more than lists

Using the function `map` to change the elements of a list has become a common pattern in modern programming languages. It is often used in Elm and `map` is also used in Elm for the Maybe and Result types.

·

By Eivind Dagsland Halderaker

·

December 23, 2021

Using the function `map` to change the elements of a list has become a common pattern in modern programming languages. It is often used in Elm and writing a function to double all the integers in a list in Elm can be written as such

``````doubleInts list =
List.map (\number -> number * 2) list``````

As expected, running the function `doubleInts [1,2,3,4]` will return `[2,4,6,8]`. You might wonder what other types map is defined for in Elm and how they operate.

## Maybe

In Elm there is a type called `Maybe` which is used when a function is not guaranteed to return a proper value. A value of `Maybe` has two variants, it can either be a value, written

``Just value``

or not a value, written as

``Nothing``

To give an example, say you have a `String` and want to convert it into a `Float` using the function `String.toFloat`, the `String` might not represent a valid floating point number. As such, it's type signature is a `String` as parameter and `Maybe Float` as it's return type.

``toFloat : String -> Maybe Float``

If given the `String` "0.5"

``String.toFloat "0.5"``

it will return the value

``Just 0.5``

as "0.5" is a valid floating point number.

On the other hand, if given the string "abc"

``String.toFloat "abc"``

the conversion to a floating point number fails and as expected the returned value is

``Nothing``

Both these value still have the type `Maybe Float` as both are valid attempts of parsing a floating point number.

The `Maybe` type is common in functional programming languages, such as Haskell's `Maybe` and Scala with it's `Option`. It is used often in functional programming instead of throwing exceptions and null-values.

The definition of the type `Maybe` in Elm is:

``````type Maybe a
= Just a
| Nothing``````

### Mapping a Maybe

When mapping a list, each element is transformed by a given function. In the case of a `Maybe`, there is either a value or not a value, and can thought of as mapping either an list with a single element or an empty list. You're hoping there's an element inside the `Maybe` box to run the function on, however, if the `Maybe` is a `Nothing`, it will just stay as a `Nothing` after mapping the function.

## How the types change

Even though mapping a function of a `Nothing` value will not change the value itself, it will still affect the type of the returned `Maybe`. Say one wants to check if the `Float` inside is greater than 5 by running

``Maybe.map (\float -> float > 5) maybeFloat``

If give an actual value

``Maybe.map (\float -> float > 5) (Just 0.5)``

The returned value still is a `Maybe`, but it's type has changed from `Maybe Float` to `Maybe Bool`.
When given a `Nothing`

``Maybe.map (\float -> float > 5) Nothing``

There is nothing to compare to, and as such the returned value will still be

``Nothing``

and it's type will be `Maybe Bool`, as we tried to compare the potential floating point value to 5.

The definition of `map` for a `Maybe` is

``````map : (a -> b) -> Maybe a -> Maybe b
map function maybe =
case maybe of
Just value ->
Just (function value)

Nothing ->
Nothing``````

## Result

Similar to `Maybe`, Elm has a type to represent values that either succeeded or gave an error which is called `Result`. It has two variants:

``Ok value``

and

``Err error``

Defined in Elm as

``````type Result error value
= Ok value
| Err error``````

Given that you want to parse a JSON object as a `String`, "{ "height": 5}".

``````parseJSON : String -> Result String Int
parseJSON JsonString = Decode.field "height" Decode.int``````

and check that the height is higher than 10 we can use `map` on the `Result` value

``````higherThan10 : Result String Int -> Result String Int
higherThan10 resultHeight = Result.map (\height -> height > 10) resultHeight``````

Similar to `Maybe`

``Result.map (\height -> height > 10) (Ok 5)``

will return

``Ok False``

and

``Result.map (\height -> height > 10) (Err "Failed to decode JSON object")``

will return

``(Err "Failed to decode JSON object")``

Here is the definition of map on a `Result`

``````map : (a -> b) -> Result error a -> Result error b
map function resultValue =
case resultValue of
Ok value ->
Ok (function value)

Err error ->
Err error``````

## Functors

The way `map` is defined for different types is inspired by the Functor design pattern in functional programming, which in turn is derived from category theory in maths. In general for a value to be a Functor it has have a function `map` with a type signature `map : (a -> b) -> f a -> f b`, where `f` is the type constructor, i. e. Maybe, Result, or List. If a functor maps the identity function, it has to return the functor unchanged.

23-Dec