Skip to content
Menu
Contents

Functions

Chapter 3 of 9

Make your own functions, and hand them to other functions like any value.

Inputs in, a value out

A function takes values in and gives a value back. Write the inputs in parentheses, then ->, then the result:

double = (n) -> n * 2
double(21)    # => 42

There is no return. A function gives back whatever its body works out to.

For longer bodies, use braces. Put the names you need first, and end with the value you want back:

describe = (n) -> {
  doubled = n * 2
  String.concat("Double is ", Int.toString(doubled))
}
describe(21)    # => "Double is 42"

Handing a function to a function

You can keep a function under a name and hand it to another function, like a number. A function can even give back a new function. Here is one that does:

makeAdder = (n) -> \(x) -> x + n
addFive = makeAdder(5)
addFive(10)    # => 15
Warning

One rule: a function goes without a backslash only directly after =. Everywhere else, write \(x) -> …. That includes inside a call, and a function given back by another function, like the \(x) in makeAdder.

Written out, the type of makeAdder has two arrows:

makeAdder : (Int) -> (Int) -> Int
makeAdder = (n) -> \(x) -> x + n

Read it as: give it an Int, and you get back a function that takes an Int and gives an Int.

Types are optional, and welcome

You can write a function's type on the line above it. The compiler checks that your code matches:

double : (Int) -> Int
double = (n) -> n * 2

Read it as: "double takes an Int and gives back an Int". It is good documentation, and it is never required, except for functions a module exports (see Modules).

Reading a type

A type is just a note about what goes in and what comes out. The inputs are in the parentheses, and the output comes after the ->:

double : (Int) -> Int
double = (n) -> n * 2

add : (Int, Int) -> Int
add = (a, b) -> a + b

sum : (List(Int)) -> Int
sum = (xs) -> List.foldl(xs, 0, \(total, n) -> total + n)

isEmpty : (String) -> Bool
isEmpty = (s) -> s == ""

greet : ({ name: String }) -> String
greet = (person) -> String.concat("Hi, ", person.name)
  • (Int) -> Int: one whole number in, one whole number out.
  • (Int, Int) -> Int: two whole numbers in, one out.
  • (List(Int)) -> Int: a list of whole numbers in, one number out.
  • (String) -> Bool: text in, yes or no out.
  • ({ name: String }) -> String: a record with a name in, text out.

A lowercase letter in a type, like the a in List(a), means "any type". List.length has the type (List(a)) -> Int: it counts a list of anything.

Choosing with if

An if needs the word then after the condition, and an else:

size = (n) -> if n > 3 then "big" else "small"
Warning

Braces after the condition do not work. if n > 3 { … } gives Expected `then` after the condition, but found `{`. Write then.

Loops are functions that call themselves

Bert has no for or while. To repeat something, a function calls itself:

sumTo = (n, total) ->
  if n == 0 then total
  else sumTo(n - 1, total + n)
sumTo(1000000, 0)    # => 500000500000

That runs a million times without trouble. If calling itself is the very last step, Bert can repeat as many times as you like without running out of memory. In first + sum(rest), the call is not the last step: the addition still has to happen after it.