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:

FunctionIts type
func Double(value: int32) -> int32func(int32) -> int32
func Ascending(a: int32, b: int32) -> boolfunc(int32, int32) -> bool
func Report(label: char8[..])func(char8[..])
func Ready() -> boolfunc() -> 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;
}
WhereWrittenCalled as
A parameterbefore: func(int32, int32) -> boolbefore(x, y)
A localvar compare: func(int32, int32) -> boolcompare(1, 2)
A fieldbefore: 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 the int instance of Larger. With no function type to take them from, as in let pick = Larger;, it is refused with generic function 'Larger' has no type arguments here.
  • A method is not a value. card.Area without parentheses is read as a field and fails with has no field 'Area'.
  • A function is not declared inside a block. A func inside a function body is function 'Helper' cannot be declared inside a function body; declare it at the top level, in a module, or in an extend block.

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

MistakeError
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 hasno overload of 'Scale' has type 'func(float64) -> float64'
A func inside a function bodyfunction 'Helper' cannot be declared inside a function body

Error messages print bool by its full name, bool8.

See also