Skip to content

Functions

Function declarations, local bindings, and the two branching forms. For literals, operators, and types see values and types; for the built-in functions see the prelude.

Declarations

A declaration is a name, zero or more parameters, =, and a body:

double x = x + x

An optional signature line may immediately precede it:

double : Int -> Int
double x = x + x

Parameters desugar to nested lambdas, so a declaration is a name bound to an expression. A declaration with no parameters is a value:

port : Int
port = 8080

Recursion

Top-level declarations may be recursive, and mutually recursive across the module — a declaration may reference any other, defined earlier or later. let bindings likewise.

isEven : Int -> Bool
isEven n = if n == 0 then True else isOdd (n - 1)
isOdd : Int -> Bool
isOdd n = if n == 0 then False else isEven (n - 1)

Recursing by accident

A top-level declaration shadows the prelude binding of the same name, and every operator desugars to a prelude binding. So this does not define addition:

add : Int -> Int -> Int
add x y = x + y

+ lowers to add, the body calls the declaration itself, and the program fails to compile:

analysis error: evaluation exceeded recursion limit (possible infinite recursion)

Every name in the Builtin column of the operator table in the prelude carries the same trap. Check a declaration name against that column before taking it; nothing in the error points back at the operator that caused it. That example is compiled by CI and asserted to keep failing this way, so if the language ever removes the trap this page fails the build rather than teaching a footgun that is gone.

Currying and partial application

Every function of more than one parameter is curried; applying fewer arguments than the arity yields a function.

plus : Int -> Int -> Int
plus x y = x + y
addFive : Int -> Int
addFive = plus 5

Application is by juxtaposition and left-associative: f a b is (f a) b.

Lambdas

\params -> body is an anonymous function:

main : List Scroll
main = List.map (\name -> scroll { name = name, glyphs = [] }) [ "a", "b" ]

A multi-parameter lambda \x y -> … desugars to nested single-parameter lambdas.

let … in

let introduces local bindings, in scope in the body after in. Each binding follows the declaration form and may carry a signature.

area : Float -> Float
area r =
let
pi = 3.14159
in
pi * r * r

Bindings may be mutually recursive and referenced in any order. let x = e in body is also valid on a single line.

if … then … else …

A conditional over a Bool. Both branches are required and must have the same type.

label : Int -> String
label n = if n > 0 then "positive" else "non-positive"

if is equivalent to a two-arm case on True / False.

See also