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.