Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Functions

Functions are defined with a name, parameters, =, and a body. All functions are first-class, automatically curried, and can be used anywhere a value is expected.

Definition

A function binding lists its parameters before =. The body is a single expression or an indented scope.

add x y = x + y
result = add 3 4
build =  {target = c, os = linux, arch = x86_64}
add = <closure>
result = 7

When the body needs local bindings, indent them under an explicit result expression:

distance x1 y1 x2 y2 = result
  dx = (x2 - x1) ** 2
  dy = (y2 - y1) ** 2
  result = dx + dy
a = distance 0 0 3 4
build =  {target = c, os = linux, arch = x86_64}
distance = <closure>
a = 25

Application

Function application is juxtaposition (space-separated), and it is left-associative: f a b means (f a) b.

add 3 4          -- 7
(add 3) 4        -- same thing

Lambdas

Anonymous functions use \params -> body.

double = \x -> x * 2
add = \x y -> x + y
a = double 5
b = add 3 4
build =  {target = c, os = linux, arch = x86_64}
double = <closure>
add = <closure>
a = 10
b = 7

Lambdas are ordinary values and appear frequently as arguments to higher-order functions.

Currying & Partial Application

Every function is automatically curried. Supplying fewer arguments than a function expects returns a new function that waits for the remaining ones.

add x y = x + y
add5 = add 5
result = add5 10
build =  {target = c, os = linux, arch = x86_64}
add = <closure>
add5 = <closure>
result = 15

This makes it natural to build specialised functions on the fly:

doubled = map (\x -> x * 2) [1, 2, 3, 4]
evens = filter (\x -> x % 2 == 0) [1, 2, 3, 4, 5, 6]
total = fold (+) 0 [1, 2, 3, 4, 5]
build =  {target = c, os = linux, arch = x86_64}
doubled = [2, 4, 6, 8]
evens = [2, 4, 6]
total = 15

Pipes & Composition

The pipe operator |> passes a value as the last argument to a function, reading left-to-right:

result = [1, 2, 3, 4, 5] \
  |> map (\x -> x * 2) \
  |> filter (\x -> x > 4) \
  |> sum
build =  {target = c, os = linux, arch = x86_64}
result = 24

Composition operators combine functions without naming an intermediate value. >> composes left-to-right and << composes right-to-left:

double x = x * 2
inc x = x + 1
double_then_inc = double >> inc
inc_then_double = inc >> double
a = double_then_inc 5
b = inc_then_double 5
build =  {target = c, os = linux, arch = x86_64}
double = <closure>
inc = <closure>
double_then_inc = <closure>
inc_then_double = <closure>
a = 11
b = 12

Recursion & Tail-Call Optimisation

Functions can reference themselves by name. Milang detects self-calls (and mutual calls) in tail position and compiles them with goto-based trampolining, so they run in constant stack space.

factorial n = if (n == 0) 1 (n * factorial (n - 1))
result = factorial 10
build =  {target = c, os = linux, arch = x86_64}
factorial = <closure>
result = 3628800

A tail-recursive accumulator style avoids building up a chain of multiplications:

fac_acc acc n = if (n == 0) acc (fac_acc (acc * n) (n - 1))
result = fac_acc 1 20
build =  {target = c, os = linux, arch = x86_64}
fac_acc = <closure>
result = 2432902008176640000

Higher-Order Functions

A higher-order function accepts or returns another function.

twice f x = f (f x)
inc x = x + 1
a = twice inc 3
b = twice (\x -> x * 2) 3
build =  {target = c, os = linux, arch = x86_64}
twice = <closure>
inc = <closure>
a = 5
b = 12

if Is a Function

Milang has zero keywords. if is an ordinary user-defined function in the prelude. It uses auto-quote parameters (#param) so the compiler automatically delays evaluation of each branch — only the chosen one runs:

if (x > 0) "positive" "non-positive"

No special syntax is needed at the call site. The if definition uses #t and #e parameters which trigger automatic quoting; inside the body, $t and $e splice (evaluate) only the selected branch. See the Metaprogramming chapter for details on auto-quote params, and Thunks & Laziness for the older ~ approach.