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.
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) listAs 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 valueor not a value, written as
NothingTo 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 FloatIf given the String "0.5"
String.toFloat "0.5"it will return the value
Just 0.5as "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
NothingBoth 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
| NothingMapping 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) maybeFloatIf 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) NothingThere is nothing to compare to, and as such the returned value will still be
Nothingand 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 ->
NothingResult
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 valueand
Err errorDefined in Elm as
type Result error value
= Ok value
| Err errorGiven that you want to parse a JSON object as a String, "{ "height": 5}".
parseJSON : String -> Result String Int
parseJSON JsonString = Decode.field "height" Decode.intand 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) resultHeightSimilar to Maybe
Result.map (\height -> height > 10) (Ok 5)will return
Ok Falseand
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 errorFunctors
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.