Function types
A function is also a value. Its type, a function type, records the parameter types and the result type and nothing else, so any function of the same shape fits wherever that type is expected. A function value can be passed as an argument, held in a local or a field, compared, and called.
function-type = "func" "(" [ parameter-type { "," parameter-type } ] ")" [ "->" result ]
parameter-type = [ name ":" ] type
The type of a function
A function's type is its declaration with the name, the parameter names and the body taken out:
| Function | Its type |
|---|---|
func Double(value: int32) -> int32 | func(int32) -> int32 |
func Ascending(a: int32, b: int32) -> bool | func(int32, int32) -> bool |
func Report(label: char8[..]) | func(char8[..]) |
func Ready() -> bool | func() -> bool |
A function type without -> describes a function with no result. Parameter names may be written inside a function type for readability, func(a: int32, b: int32) -> bool, but they are ignored: only the types count.
Two function types are the same when their parameter types and result types are identical, in order. Nothing converts — a func(int) -> int does not fit a func(int32) -> int32, and a function returning bool does not fit one returning int32.
Function values
A function's name, written without parentheses, is a value of its function type. Writing parentheses calls the function instead.
type Comparator = func(int32, int32) -> bool;
struct Sorter {
name: char8[..];
before: Comparator;
}
func Ascending(a: int32, b: int32) -> bool {
return a < b;
}
func Descending(a: int32, b: int32) -> bool {
return a > b;
}
func Sort(items: var int32[..], before: func(int32, int32) -> bool) {
for i in 1..items.length {
var j = i;
while j > 0 && before(items[j], items[j - 1]) {
let held = items[j];
items[j] = items[j - 1];
items[j - 1] = held;
j -= 1;
}
}
}
func Main() -> int {
var numbers: int32[5] = [3, 1, 4, 1, 5];
Sort(numbers[..], Ascending); // 1 1 3 4 5
let sorter = Sorter { name: "down", before: Descending };
Sort(numbers[..], sorter.before); // 5 4 3 1 1
var compare: func(int32, int32) -> bool = Ascending;
PrintLine("{}", compare(1, 2)); // true
compare = Descending;
PrintLine("{}", compare(1, 2)); // false
PrintLine("{}", compare == Descending); // true
return 0;
}
| Where | Written | Called as |
|---|---|---|
| A parameter | before: func(int32, int32) -> bool | before(x, y) |
| A local | var compare: func(int32, int32) -> bool | compare(1, 2) |
| A field | before: Comparator; | sorter.before(x, y) |
A local, parameter or field of function type is called exactly like a function, and the call runs whichever function the value holds at that moment. A var local or field can be reassigned to another function of the same type. == and != compare two function values by identity: they are equal when they hold the same function.
A type alias gives a long function type a name. The alias is the same type, so Comparator and func(int32, int32) -> bool are interchangeable, as Sort and Sorter show.
What a function value is not
A function value refers to one declared function and carries no state of its own.
- There are no closures. There is no lambda or anonymous function expression, and a function value cannot capture a local variable. Data a callback needs is passed to it as an argument, or kept in a struct beside the function field.
- A generic function is not a value. Its name has no single type until its type parameters are known, so
let pick: func(int, int) -> int = Larger;is refused withcannot assign 'func(T, T) -> T' to 'func(int, int) -> int'. Pass a non-generic function that calls it. - A method is not a value.
card.Areawithout parentheses is read as a field and fails withhas no field 'Area'.
rux 0.4.0 does not yet reject a
func declared inside a function body, and a program that calls one builds but cannot start. Declare functions at the top level, in a module, or in an extend block.rux 0.4.0 does not yet choose an overload by the expected function type: the name of an overloaded function is taken as one particular overload, and assigning it to a variable of another overload's type fails with
cannot assign. Pass functions that are not overloaded.Rules and errors
| Mistake | Error |
|---|---|
A function of another shape, Sort(numbers[..], IsEven) | argument 2 to 'Sort' has type 'func(int32) -> bool8', but parameter 'before' requires 'func(int32, int32) -> bool8' |
Calling instead of passing, Sort(numbers[..], Ascending(1, 2)) | argument 2 to 'Sort' has type 'bool8', but parameter 'before' requires 'func(int32, int32) -> bool8' |
Calling a function value with the wrong arguments, compare(1) | call to 'compare' expects 2 arguments, but 1 was provided |
Error messages print bool by its full name, bool8.
See also
- Function declarations — declaring the functions that become values
- Type aliases — naming a function type
- Structs — fields of function type
- Learn: Callback, Function field