Functions in langur are pure in that they are not allowed to set values outside of their boundaries. They could still have "side effects," such as using writeln().

Functions are first-order. Both user-defined functions and built-in functions can be passed around as values.

Curly braces are not required for a function declaration if the function body is a single statement.

The f token designates a function definition.

Functions may be defined with explicit parameters, no parameters, or implied parameters, as shown in the examples below.

f(.x, .y) { .x + .y } # 2 explicit parameters f() { .x + .y } # no parameters (closure on 2 values) f { .x + .y } # 2 implied parameters

User-defined functions can be called with parentheses, such as .x().

Bulit-in functions can be called with either parentheses or an unbounded argument list, such as split re/[!_]/, "a!b_c" which would split the string "a!b_c" into an array of 3 strings.

An unbounded list cannot be used in the test expression of an if expression (must use parentheses for function call in this case).

Unbounded lists end at a line return, end of file, or closing parenthesis, curly brace, or square bracket.

If you do not use a list or parentheses on a function name, it is not a call, but a reference to the function.

Functions defined to use values outside their boundaries are closures. After a closure is defined, it is not affected by changes to those outside values.

Closures cannot be combined with implied parameters. It could only be one or the other.

Parameters are implied by having no parentheses directly attached to the f token. For example, f (.x - '0') ^ (.x - '0') is a function that has no explicit parameters (1 implied).

The implied order of implied parameters is based on the string sort order of their names, not on their order of appearance in the function.

Implied parameters are convenient for small functions, such as in the following example.

val .factorial = f if(.x < 2: 1; .x x self(.x - 1))

Recursion can be used when the function definition is directly assigned to a variable, such as...

val .fibonacci = f(.x) if .x < 2 {
.x
} else {
.fibonacci(.x - 1) + .fibonacci(.x - 2)
}

This example function uses recursion and a single statement (if expression) without curly braces (braces shown are part of the if expression, not for the function).

Recursion can also be used by making a call with the self token.

val .fibonacci = f if .x < 2 { .x } else { self(.x - 1) + self(.x - 2) } # or shortened to... val .fibonacci = f if(.x < 2: .x ; self(.x - 1) + self(.x - 2))

Using the self token, recursion does not require assignment. This also makes it possible to combine implied parameters and recursion, which you cannot do when using the name.

Note that the map(), fold(), and foldfrom() functions, or for loops, can often be used instead of recursion.

An explicit return can be used, but a function's last value is its implicit return value.

All functions return something, even if it's nothing (null).

Sometimes using parentheses and explicit parameters will give more clarity. For example, I could write...

val .factorial = f fold f .a x .b, series 1 to .n

...but this might be more clearly written as...

val .factorial = f(.n) fold(f .a x .b, series 1 to .n)

- .n not an implied parameter of .factorial()
- fold() using parentheses
- still no parentheses on series(), but it's clear
- still have implied parameters .a and .b on the anonymous function, but it's not confusing