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 on its own. Its name has no single type until its type parameters are known, so it takes them from the function type it is given:
let pick: func(int, int) -> int = Larger;is theintinstance ofLarger. With no function type to take them from, as inlet pick = Larger;, it is refused withgeneric function 'Larger' has no type arguments here. - A method is not a value.
card.Areawithout parentheses is read as a field and fails withhas no field 'Area'. - A function is not declared inside a block. A
funcinside a function body isfunction 'Helper' cannot be declared inside a function body; declare it at the top level, in a module, or in anextendblock.
Overloaded names
The name of an overloaded function, used as a value, is the overload whose function type its destination expects: an annotated binding, an assignment, a function-typed parameter of a callee with one declaration, or a return in a function with a function-typed result. A module path such as Tools::Scale is chosen the same way.
func Scale(value: int32) -> int32 {
return value * 2;
}
func Scale(value: float32) -> float32 {
return value * 2.0f32;
}
func Main() -> int {
let whole: func(int32) -> int32 = Scale; // the int32 overload
let real: func(float32) -> float32 = Scale; // the float32 overload
PrintLine("{} {}", whole(3), real(1.5f32)); // 6 3.0
return 0;
}
With no function type to choose by, as in let f = Scale;, the name is refused, and so is a type that no overload has:
error: overloaded function 'Scale' has no function type here to choose an overload by
note: overload 'Scale' has type 'func(int32) -> int32'
note: overload 'Scale' has type 'func(float32) -> float32'
help: give the value a function type, as in 'let f: func(int32) -> int32 = Scale;'
error: no overload of 'Scale' has type 'func(float64) -> float64'
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 |
An overloaded name with no type to choose by, let f = Scale; | overloaded function 'Scale' has no function type here to choose an overload by |
| A function type no overload has | no overload of 'Scale' has type 'func(float64) -> float64' |
A func inside a function body | function 'Helper' cannot be declared inside a function body |
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