langur

function parameters

Functions with parameters may be defined as shown in the examples below.

fn(x, y) { x + y } fn x, y: x + y

Functions without parameters may be defined as shown in the examples below.

fn() { 123 } fn: 123

When using parentheses to define parameters (long form), curly braces are required to define the function body. The simple, or short form, without parentheses, requires a colon between parameters and the body of the function. With the short form, curly braces are not required if the body of the function is a single expression.

parameter mutability

By default, function parameters are immutable.

To make a parameter mutable, precede the parameter name with a var token within the parameter list.

fn(x, y) { ... } # x and y immutable

fn(var x, y) { ... } # x mutable and y immutable

fn(var x, var y) { ... } # x and y mutable

parameter categories

Langur parameters fall into the following categories.

parameters by name

You can define optional parameters after positional parameters using the assignment syntax to define a default value for them.

val addto = fn(a, b=3) { a + b }

Optional arguments are always passed with the name (using assignment syntax), and positional arguments are never passed with the name.

addto(1)

addto(1, b=12)

Arguments using the name are not order-dependent (as long as they come after positional arguments), and any of them can be left out (except for required parameters by name).

An external name of a parameter by name can be preserved while changing the internal variable name, using the as keyword. An external name specified this way may also be the same as a keyword, since the context makes the meaning clear. The internal name is used within the function and the external name is used in the call.

val x = fn(a as c=123) { a } # a used internally x(c=9) # c used in call

This allows you to change a parameter name internally and not break an API in the process.

Parameters by name should not be confused with argument/parameter expansion, which only applies to positional arguments and parameters.

required parameters by name

You can require a parameter that also must use the name (not positional). To do this, use the alias, but do not specify a default value. If you specify a default value, it is an optional parameter.

val x = fn(a as a) { a } # no default value for a; not optional and not positional x(a=9) # a required in call

argument expansion

You can use a ... operator on the last positional argument passed to a function, to expand a list argument into multiple arguments. This precedes any optional arguments passed.

val x = [[21, 7], [42, 14], [96, 21]] zip(x...) # result == [21, 42, 96, 7, 14, 21]

zip(mapX([7], [14, 21], by=fn ... x: x)...) # result == [7, 7, 14, 21]

parameter expansion

You can use the ... operator for parameter expansion. This allows a user-defined function to receive a variable number of arguments. The last arguments of the function will be placed into a single list. This also allows for setting limits for this expansion (with a default range of 0 to unlimited). -1 indicates unlimited for this purpose. See the following examples.

val a = fn(... x) { } # accepts 0 or more arguments val b = fn(...[1..9] x) { } # accepts 1 to 9 arguments val c = fn(x, ...[4] y) { } # accepts 5 arguments val d = fn(x, ...[2 .. ] y) { } # accepts 3 or more arguments val e = fn(x, ...[2 .. 6] y) { } # accepts 3 to 7 arguments

Argument/parameter expansion is considered to be positional and does not conflict with parameters by name.