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 may 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.
We can now call this function with different velocities to find out whether or not a spaceship can leave the Earth.
if expression, we can assign the value returned by a function to a constant.
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.
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.
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
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.
- 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
The first two lines define a new module called
Playground and import the
Html package. Don’t worry about
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
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:
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
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:
Yikes! Chaining multiple function applications looks hideous and hard to read. But what if we write it this way instead:
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.
When we apply it to two arguments, we get the expected result.
But, what if we only give it the first argument?
Hmm… It returns a function instead of giving us an error. Let’s capture that function in a constant.
Let’s see what happens if we apply this intermediate function to the second (and final) argument.
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. Since that name is too long, the Elm community refers to it as the pipe operator. It is very useful for avoiding parentheses. It pipes the result from previous expression to the next one.
The pipe 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
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:
Ugh. Even more parentheses. Refresh the page at
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.
Let’s use our knowledge of the
|> operator to turn the expression in
main into a beautiful chain.
We can also write it using the
Not bad, huh? How about this:
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:
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
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.
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.
Elm doesn’t like that.
What about normal functions? Can we include a special character in their name?
Nope. Elm doesn’t like that either. Before moving on, you should remove the invalid operator and function definitions (listed above) from
Number of Arguments
Operators accept exactly two arguments whereas there is no limit to how many arguments a normal function can have.
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.
Normal functions are applied by writing the function name, followed by its arguments. This style of application is called prefix-style.
We can also apply operators in prefix-style if we so choose to.
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