Elm is a functional language, so naturally, functions and function calls are pretty important. We have already seen some functions in the previous episodes. This episode goes into more detail regarding function definition and function application.
About This Series
This is the fifth post in a series of short and sweet blog posts about Elm. The stated goal of this series is to take you from “completely clueless about Elm” to “chief Elm guru”, step by step. If you have missed the previous episodes, you might want to check out the table of contents.
This is a function definition in Elm:
multiply a b = a * b
The equals character
= separates the function name with the parameter list from the function body. The function body can be any Elm expression that produces a value. We can reference the parameters in the function body.
Function definitions can use other functions in their body. This is how this looks like:
square a = multiply a a
Here, we define a
square function by using the
multiply function we defined earlier. Function calls also don’t need any parentheses, just list the function parameters you want to pass into the function separated by whitespace.
You do need parentheses when you have nested expresssions:
productOfSquares a b = multiply (square a) (square b)
You can also declare anonymous functions (also known as lambda expressions) on the fly:
incrementAll list = List.map (\ n -> n + 1) list
This thing wrapped in
) is an anonymous function that takes one parameter and returns the parameter, incremented by one. This anonymous function is then applied to all elements in a list by using
Actually, we could have written this shorter. The following is equivalent:
incrementAll2 = List.map (\ c -> c + 1)
Why is that the same? Because Elm supports something called currying.
incrementAll defines a function that takes a list and produces another list.
incrementAll2 also defines a function, but it is a function that takes no arguments and returns another function.
So when we write
incrementAll2 [1, 2, 3] Elm first evaluates
incrementAll2, gets a function and then procedes to put the remaining arguments (
[1, 2, 3] in this case) into this function. The result is the same.
If you find it hard to wrap your head around currying, don’t worry about it too much for now. You can always resort to write a more verbose version of your function without currying and come back to this concept later. As a rule of thumb, if the last element in the parameter list in the function declaration is simply repeated at the end of the function body (like
list in this case), you can probably omit both.
Let’s wrap this up. Here is a complete, working Elm program that uses the functions we defined above:
import Html multiply a b = a * b square a = multiply a a productOfSquares a b = multiply (square a) (square b) incrementAll list = List.map (\ c -> c + 1) list incrementAll2 = List.map (\ c -> c + 1) main = Html.p  [ Html.text ("3 × 5 = " ++ (toString (multiply 3 5))) , Html.br   , Html.text ("4² = " ++ (toString (square 4))) , Html.br   , Html.text ("2² × 3² = " ++ (toString (productOfSquares 2 3))) , Html.br   , Html.text ("incrementAll [1, 2, 3] = " ++ (toString (incrementAll [1, 2, 3]))) , Html.br   , Html.text ("incrementAll2 [1, 2, 3] = " ++ (toString (incrementAll2 [1, 2, 3]))) ]
What happens in these lengthy expressions in the
main function? Well, the functions we defined return mostly numbers (or lists, in the case of incrementAll). So we need to convert their results into strings via the toString function (which comes from the
Basics package and is imported by default). We then use
++ to append the resulting string to a string literal (
"3 × 5 = ", for example) and use
Html.text to convert the string into an HTML text node.
Fancy Function Application
Whoa, did you see what we did there to bring the result of one of our functions to the screen? Let’s take a look at
Html.text ("3 × 5 = " ++ (toString (multiply 3 5))) for a moment. That’s a lot of parentheses right there. Elm has two operators,
<|, to write expressions like that in a more elegant fashion.
|>: Take the expression to the left of the operator and put it into the function on the right hand side.
<|: Take the expression to the right of the operator and put it into the function on the left hand side.
Here is the main function of the previous program, rewritten with the new operators:
main = Html.p  [ Html.text <| "3 × 5 = " ++ (toString <| multiply 3 5) , Html.br   , Html.text <| "4² = " ++ (toString <| square 4) , Html.br   , Html.text <| "2² × 3² = " ++ (toString <| productOfSquares 2 3) , Html.br   , Html.text <| "incrementAll [1, 2, 3] = " ++ (toString <| incrementAll [1, 2, 3]) , Html.br   , Html.text <| "incrementAll2 [1, 2, 3] = " ++ (toString <| incrementAll2 [1, 2, 3]) ]
If you like to go a bit crazy with this, you can even rewrite
Html.text <| "3 × 5 = " ++ (toString <| multiply 3 5)
Html.text <| (++) "3 × 5 = " <| toString <| multiply 3 5
square 4 |> toString |> (++) "4² = " |> Html.text
Here we used the infix operator
++ as a non-infix function to be able to apply it with
|>. We also used a bit of currying again:
(++) actually takes two arguments (the two strings that are to be concatenated). The expression
(++) "3 × 5 = " is a partial function application, that is, we provide the first of the two arguments to yield a new function that takes only one argument, prepending
"3 × 5 = " to everything that is passed to it.
To read code like this with ease, just imagine this line as the ASCII art represenation of a data pipeline. In
|> style pipelines, data flows from left to right, in
<| style pipelines, data flows from right to left. So, for example, to decipher a line like
Html.text <| (++) "3 × 5 = " <| toString <| multiply 3 5, you start at the end (
multiply 3 5), push this into the
toString function to convert the number into a string, the resulting string the goes into the append function (
(++)) together with the string literal and finally the concatenated string goes into the
This concludes the fifth episode of this blog post series on Elm. Make sure to check out the next episode, where we will take a look at type annotations.