Functions

ton.function add(a, b) {
    return a + b
}
print(add(2, 3))   // 5

Functions are values: they can be stored in variables, passed as arguments, and returned from other functions. Function expressions create one inline, most often as a callback:

ton.array doubled = map([1, 2, 3], ton.function(n) { return n * 2 })

A function expression can optionally carry a name (ton.function label(n) { ... }) used only for friendlier debugger/error output — it isn't bound as a variable by that name.

Default parameters — give a parameter = expression to make it optional; the default is evaluated at call time (in order, so a later default can reference an earlier parameter) and only when the argument is omitted:

ton.function greet(name, greeting = "Hello") {
    return greeting + ", " + name + "!"
}
print(greet("kuro"))            // Hello, kuro!
print(greet("kuro", "Salut"))   // Salut, kuro!

ton.function pair(a, b = a + 1) {
    return str(a) + "-" + str(b)
}
print(pair(5))   // 5-6

Rest parameters — prefix the last parameter with ... to collect any extra arguments into an array:

ton.function sum(...nums) {
    ton.int total = 0
    for ton.n in nums { total += n }
    return total
}
print(sum(1, 2, 3, 4))   // 10
print(sum())              // 0

A function can mix fixed and rest parameters (function label(prefix, ...items) { ... }), but ... may only appear on the last one.

Calling a function with too few required arguments (or too many, when there's no rest parameter) is a runtime error — the message adapts to whether the function has defaults/rest ('ton.f' expects 2 argument(s) but got 1., 'ton.f' expects between 1 and 2 argument(s) but got 0., 'ton.f' expects at least 1 argument(s) but got 0.).

Closures work as expected: a function remembers the variables in scope where it was declared, even after that scope has otherwise finished.

Arrow functions (since beta-2.0.0) — a short way to write a function expression, ideal for callbacks. A single parameter needs no parentheses; an expression body is returned automatically; a { block } body works like a normal function.

ton.let double = n => n * 2
ton.let add = (a, b = 1) => a + b
ton.let greet = (name) => {
    ton.string msg = "Hello " + name
    return msg
}
print(double(21), add(1), greet("kuro"))   // 42 2 Hello kuro

Spread in calls — f(...args) passes the elements of an array as separate arguments: max(...[4, 9, 2]).

The pipe operator |> — x |> f calls f(x), and x |> f(a, b) calls f(x, a, b). It turns nested calls into a left-to-right pipeline:

ton.function inc(x, by = 1) { return x + by }
print(5 |> inc |> inc(10) |> str)              // 16
[1, 2, 3, 4] |> filter(n => n % 2 == 0) |> sum |> print   // 6

print is also a function value: ["a", "b"].forEach(print). print(a, b, c) prints its arguments separated by spaces.

Callbacks given to map, filter, find, findIndex, forEach, any, all, count and flatMap receive the index as a second argument when they declare two parameters: names.map((name, i) => f"{i}. {name}").

Deep recursion is fine (the interpreter runs on a large stack), and runaway recursion stops with a catchable Maximum recursion depth (10000) exceeded error instead of crashing.

Edit this page on GitHub