Function
- Can return nothing or a value
- Can modify the data in the database when called from an operation (run-time verification)
- Can get called from queries, operations, or functions
- If the return type isn't specified explicitly, it's a
unit
(no return value)
Short form
function f(x: integer): integer = x * x;
Full form
function f(x: integer): integer {
return x * x;
}
Return type not specified
When the return type isn't specified, it's considered a unit
:
function f(x: integer) {
print(x);
}
Default parameter values
Parameters of functions can have default values. The default parameters get used if you don't specify the parameters in the function call.
function f(user: text = 'Bob', score: integer = 123) {...}
...
f(); // means f('Bob', 123)
f('Alice'); // means f('Alice', 123)
f(score=456); // means f('Bob', 456)
Named function arguments
One could also specify function arguments by names.
function f(x: integer, y: text) {}
...
f(x = 123, y = 'Hello');
Using a function as a value
If you want to pass a function to another function, then you can use the function as a value by using the following syntax:
() -> boolean
(integer) -> text
(byte_array, decimal) -> integer
Within the parentheses, you specify the function input type of the passed function after the arrow follows the function's return type.
An example could look like this:
function filter(values: list<integer>, predicate: (integer) -> boolean): list<integer> {
return values @* { predicate($) };
}
Partial function app
You can use the wildcard symbol *
to create a reference to a function (to obtain a value of a function).
function f(x: integer, y: integer) = x * y;
val g = f(*); // Type of "g" is (integer, integer) -> integer
g(123, 456); // Invocation of f(123, 456) via "g".
Extendable functions
You can declare a function as extendable by adding @extendable
before the function declaration. You can define an arbitrary number of extensions for an extendable function by expressing @extend
before the function declaration.
In the example below, function f
is a base function, and functions g
and h
are extension functions.
@extendable function f(x: integer) {
print('f', x);
}
@extend(f) function g(x: integer) {
print('g', x);
}
@extend(f) function h(x: integer) {
print('h', x);
}
When you call the base function, all its extension functions get executed, and the base function itself gets executed. However, Extendable functions support a limited set of return types, and this behavior depends on the return type.
The following behavior applies to the different return types:
Unit
- All extensions get executed.
- The base function is always executed at the end.
Boolean
- Extensions get executed individually until some of them return "true."
- The base function gets executed if all extensions return "false."
- The result of the last executed function gets returned to the caller.
T?
- Similar to a boolean. Extensions execute until the first non-null result is returned to the caller.
list<T>
- All extensions get executed.
- The base function gets executed at the end.
- The concatenation of all lists gets returned to the caller.
map<K, V>
- Similar to list<T>, the union of all maps gets returned to the caller but fails if there is a key conflict.