Skip to the content.

Functions

Declaring a function

Functions are declared with let, just like variables — a function is a value:

let add(a, b) = do
    return a + b

term.log(add(2, 3))   # 5

For a single-expression body, you can skip do and return entirely:

let square(x) = x * x

This is the same pattern used for one-line class methods (see Classes and objects).

Anonymous (lambda) functions

Functions don’t need a name. An anonymous function is written the same way, just without an identifier before the parameter list:

term.log(()=10)          # a zero-argument anonymous function returning 10
term.log((x)=10)
term.log((x,y)=10)
term.log((x,y,z)=10)

# named versions, for comparison:
term.log(a()=10)
term.log(b(x)=10)

(from anonymous_function.ar)

Anonymous functions are commonly passed directly as arguments, e.g. to array.map:

term.log(
  array(range(-1000000, 1000000))
    .map((val) = val * val)
    .filter((val) = val < 10000)
    .sort((a, b) = a < b ? -1 : a > b ? 1 : 0)
)

(from map_filter_sort.ar — this also shows method chaining on arrays; see Arrays.)

Default arguments

let round(num, precision=1) = do
    ...

(from stdlib/maths/init.ar)

Variadic arguments: *args and **kwargs

A function can collect any extra positional arguments into an array with *args, and any extra keyword arguments into a dictionary with **kwargs:

let f(*args, **kwargs) = do
    term.log(args, kwargs)

f("Hello world", *[1, 2, 3], **{x: 1, y: 5, z: 7})

(from argument_unpacking.ar) — note that * and ** are also used at the call site to unpack an existing array/dictionary into positional/keyword arguments, exactly like the definition-site collectors.

Mixing required, default, *args, and **kwargs

let f(x, a=null, *args, **kwargs) = do
    return `x=$(x), a=$(a), args=$(args), kwargs=$(kwargs)`

term.log(f(1))
term.log(f(1, 2))
term.log(f(1, 2, 3))
term.log(f(a=2, *["this should go to x"]))
term.log(f(1, a=2, **["this should go to kwargs"]))
term.log(f(1, a=2, **{wow: "this should go to kwargs"}))
term.log(f(a=2, **{x: "this should go to x"}))

(from test_mixed_args_defaults_and_unpacking.ar)

This shows that keyword arguments can fill in named parameters like x even when passed via **{x: ...}, and that unpacking a plain array with ** still works, filling in kwargs positionally in the array’s order.

Recursion

Functions can call themselves normally:

let factorial(x) = do
    let n = x - 1
    if (n) return x * factorial(n)
    return 1

(from factorial.ar)

Closures

Functions capture variables from their enclosing scope:

let f() = do
    let x = 10
    let i = 0
    while (i < 100) do
        i = i + 1
        ...

let g() = f()

g()

(from loop_continue_and_break_test.ar)

Functions as methods on built-in types

You can attach a method directly to a built-in type, e.g. string:

string.cool(self) = do
    term.log(self, self.length)
    return 10

'hello world'.cool()
string.cool('goodbye world')

(from class_method.ar) — note the two equivalent call styles: as an instance method ('hello world'.cool()) or as an explicit call on the type passing self (string.cool('goodbye world')).

Next