3.10

Function

The good old space race is back on. Wouldn’t it be great if we could help out some of these incredibly ambitious companies by letting them reuse our code to find out whether or not their spaceship can leave the Earth too? We can do that by creating a function that encapsulates our logic. Before we do that, let’s understand what a function is in mathematics first.

You might be wondering why I keep taking you back to the wonderful world of mathematics every time I try to explain a concept in Elm. That’s because Elm is a functional programming language which gives it a strong mathematical foundation. Elm implements concepts from mathematics without distorting them. So by understanding how they work in mathematics first, it’s easier to understand them later in Elm. As a bonus, you know where these concepts originally came from. This doesn’t mean you need to have a strong background in mathematics to learn Elm. Everything you need to learn Elm is covered in this book including the mathematical concepts.

A function, in mathematics, is a relationship from a set of inputs to a set of possible outputs where each input is mapped to exactly one output. Functions in Elm also work the same way.

Let’s write a function in Elm called escapeEarth that takes a value from an input set of velocities and maps it to a value from an output set of instructions.

> escapeEarth velocity = \
|   if velocity > 11.186 then \
|     "Godspeed" \
|   else \
|     "Come back"
<function>

We can now call this function with different velocities to find out whether or not a spaceship can leave the Earth.

> escapeEarth 11.2
"Godspeed"

> escapeEarth 11
"Come back"

Like if expression, we can assign the value returned by a function to a constant.

> whatToDo = escapeEarth 11.2
"Godspeed"

> whatToDo
"Godspeed"

Function Syntax

Functions are so critical to Elm applications that Elm provides an incredibly straightforward syntax for creating them. No ceremony is required, such as using a special keyword or curly braces which is common in many languages.

Function Application

Once we create a function, we need to apply it to a value to get a desired output. In mathematics, function application is the act of applying a function to a value from the input set to obtain a corresponding value from the output set. You have already seen an example of a function application above: escapeEarth 11. When a function is applied, its name should be separated from its argument with whitespace. If a function takes multiple arguments, they too should be separated from each other with whitespace.

Parameter vs Argument
The terms parameter and argument are often used interchangeably although they are not the same. There is no harm in doing so, but just to clear things up, an argument is used to supply a value when applying the function (e.g. escapeEarth 11) whereas a parameter is used to hold onto that value in function definition (e.g. escapeEarth velocity).

Earlier we learned that all Elm applications are built by stitching together expressions. How do functions fit into this organizational structure? Functions are values too like numbers and strings. Since all values are expressions, that makes functions expressions as well. Because of this, functions can be returned as a result of a computation. They can be passed around like any other value. For example, we can give them to another function as an argument. Functions that take other functions as arguments or return a function are called Higher Order Functions. We will see many examples of higher order functions soon.

Functions with Multiple Parameters

The escapeEarth function above takes only one argument: velocity. But, we can give it as many arguments as we want. Let’s add one more parameter to its definition so that it can tell us whether or not our horizontal speed is fast enough to stay in an orbit.

The next code we will write is a bit difficult to type in the repl without making any mistakes. So we will write it in a file instead. Create a new directory called elm-examples in the root directory (beginning-elm) of the Elm project we created in the last chapter. Inside that directory create a file named Playground.elm. We will use this file to experiment with various concepts in Elm. Here is how the directory structure should look like so far.

Filename
Elm style conventions dictate that filenames are also written in Camel Case. Unlike constants, the first letter in a filename should be written in uppercase though. The first letter of each subsequent concatenated word should be capitalized. Elm doesn’t throw an error if we don’t follow this convention, but there is no reason not to follow it.

Put the following code in Playground.elm.

module Playground exposing (..)

import Html


escapeEarth velocity speed =
    if velocity > 11.186 then
        "Godspeed"
    else if speed == 7.67 then
        "Stay in orbit"
    else
        "Come back"


main =
    Html.text (escapeEarth 11.2 7.2)

The first two lines define a new module called Playground and import the Html package. Don’t worry about module and import yet; we’ll cover them later. Next is our function escapeEarth. We give it one more parameter called speed. If the velocity is not greater than 11.186, it will use the else if branch to compare if the speed is equal to 7.67. If yes, it returns “Stay in orbit”. Otherwise, it falls to the else branch.

The last function is main. The execution of all Elm applications start with this function. It’s a regular function like any other. It just happens to be the entry-point for an application. We apply a function called Html.text to the result produced by yet another function application: escapeEarth 11.2 7.2. Html.text takes a string and displays it on a browser. It’s important to surround escapeEarth and its arguments with parentheses. Otherwise, Elm will think we are passing three arguments to Html.text which takes only one argument. This is because Elm uses whitespace to separate a function name from its arguments. We can chain as many function applications as we want.

Go to the beginning-elm directory in your terminal and run elm-reactor. After that open this URL on a browser: http://localhost:8000/. You should see elm-examples as one of the directories listed in File Navigation section. Click on it. You should see the Playground.elm file. If you click that file too, elm-reactor will compile the code in it and display the result. You should see the string “Godspeed” on a browser.

Partial Function Application

When we applied the escapeEarth function above, we pulled the value for speed out of thin air. What if it’s actually computed by another function? Add the following function definitions right above main.

speed distance time =
    distance / time


time startTime endTime =
    endTime - startTime


main =
    ...

speed takes two parameters: distance covered by a spaceship and travel time. Time in turn is computed by another function called time. Here’s how the main function looks when we delegate the calculation of speed to our newly created functions:

main =
    Html.text (escapeEarth 11 (speed 7.67 (time 2 3)))

Yikes! Chaining multiple function applications looks hideous and hard to read. But what if we write it this way instead:

main =
    time 2 3
        |> speed 7.67
        |> escapeEarth 11
        |> Html.text

Ah, much better! To compile the new code, refresh the page at http://localhost:8000/elm-examples/Playground.elm. You should see the string “Stay in orbit”.

The nicely formatted code above is possible because Elm allows partial application of functions. Let’s play with some examples in repl to understand what partial functions are. Create a function called multiply with two parameters in the repl.

> multiply a b = a * b
<function>

When we apply it to two arguments, we get the expected result.

> multiply 3 4
12

But, what if we only give it the first argument?

> multiply 3
<function>

Hmm… It returns a function instead of giving us an error. Let’s capture that function in a constant.

> multiplyByThree = multiply 3
<function>

Let’s see what happens if we apply this intermediate function to the second (and final) argument.

> multiplyByThree 4
12

> multiplyByThree 5
15

multiplyByThree is a partial function. When Elm sees that we haven’t given enough arguments to a function, instead of complaining it applies the function to given arguments and returns a new function that can be applied to the remaining arguments at a later point in time. This has many practical benefits. You will see plenty of examples of partial function application throughout the book.

Forward Function Application

Let’s get back to that fancy |> operator that made our code look so pretty. It’s called the forward function application operator. It is very useful for avoiding parentheses. It pipes the result from previous expression to the next one.

The forward function application operator takes the result from the previous expression and passes it as the last argument to the next function application. For example, the first expression in the chain above (time 2 3) generates number 1 as the result which gets passed to the speed function as the last argument.

Backward Function Application

There is another operator that works similarly to |>, but in backward order. It’s called the backward function application operator and is represented by this symbol: <|. Let’s deviate from our spaceship saga for a bit and create some trivial functions to try out the <| operator. Add the following function definitions right above main.

add a b =
    a + b


multiply c d =
    c * d


divide e f =
    e / f


main =
    ...

add, multiply, and divide are functions that do exactly what their names suggest. Let’s create an expression that uses these functions. Modify the main function to this:

main =
    Html.text (toString (add 5 (multiply 10 (divide 30 10))))

Ugh. Even more parentheses. Refresh the page at http://localhost:8000/elm-examples/Playground.elm and you should see 35. The add function returns a number, but Html.text expects a string. That’s why we need to use the toString function to convert a number into a string. It turns any kind of value into a string.

> toString 42
"42"

> toString 7.8
"7.8"

> toString [5, 10]
"[5,10]"

> toString { a = 3, b = 6 }
"{ a = 3, b = 6 }"

Let’s use our knowledge of the |> operator to turn the expression in main into a beautiful chain.

main =
    divide 30 10
        |> multiply 10
        |> add 5
        |> toString
        |> Html.text

We can also write it using the <| operator.

main =
    Html.text <| toString <| add 5 <| multiply 10 <| divide 30 10

Not bad, huh? How about this:

main =
     Html.text <|
      toString <|
         add 5 <|
   multiply 10 <|
  divide 30 10

Working backwards to forwards can be confusing. Just because you can do it in Elm, doesn’t mean you should. If you feel like you’re in Bizarro World, where down is up and up is down, go ahead and stick with the forward application (|>). Elm provides other higher-order helper operators like these. You can learn more about them here.

Operators are Functions Too

In Elm, all computations happen through the use of functions. As it so happens, all operators in Elm are functions too. They differ from normal functions in three ways:

Naming

Operators cannot have letters or numbers in their names whereas the normal functions can. +++ is an illegal operator in Elm, but we can legitimize it by defining it ourselves. Add the following definition right above main in Playground.elm

(+++) first second =
    first ++ second


main =
    ...

We have given +++ the same behavior as the ++ operator which is already defined in Elm. ++ is used to concatenate two strings. Notice how the operator is surrounded by parentheses in its definition. We have to do that when we define custom operators using the function syntax. By default, custom operators have the highest precedence (9) and are left-associative. Like any other built-in operator, we can apply +++ to its arguments.

main =
    Html.text ("Peanut butter " +++ "and jelly")

If you refresh the page at http://localhost:8000/elm-examples/Playground.elm, you should see “Peanut butter and jelly”. Let’s see what happens if we add a letter to a custom operator’s name.

(+a+) first second =
    first ++ second


main =
    ...

Elm doesn’t like that.

What about normal functions? Can we include a special character in their name?

ad+d a b =
    a + b


main =
    ...

Nope. Elm doesn’t like that either. Before moving on, you should remove the invalid operator and function definitions (listed above) from Playground.elm.

Number of Arguments

Operators accept exactly two arguments whereas there is no limit to how many arguments a normal function can have.

Application Style

Operators are applied by writing the first argument, followed by the operator, followed by the second argument. This style of application is called infix-style.

> 2 + 5
7

Normal functions are applied by writing the function name, followed by its arguments. This style of application is called prefix-style.

> add a b = a + b
<function>

> add 2 5
7

We can also apply operators in prefix-style if we so choose to.

> (+) 2 5
7

The operators must be surrounded by parentheses. We can’t apply normal functions in infix-style. But, we can create something that resembles infix-style by using |>.

> 2 |> add 5
7
Back to top

New chapters are coming soon!

Sign up for the Elm Programming newsletter to get notified!

* indicates required
Close