Silex v0.47.1 / Docs Canonical source ↗

Pass a callback function

A function can become a value, be stored in a field, or be passed to another function. Its type describes its parameter modes and return type.

Name an existing function

func any<T>(values:T[], predicate:func(@T) bool) bool {
    for value in values {
        if predicate(value) {
            return true
        }
    }
    return false
}

func positive(value:@int) bool { return value > 0 }

func main() {
    print(any<int>([-1, 2], positive))
}

Write func(T) for a callback returning void, and func(T) R for one returning R. Parameters retain ordinary Silex modes: value, read reference @, or mutable reference &. The expected type selects an overloaded function.

Bind a method to its instance

An instance method can be extracted from its receiver. The resulting value keeps that receiver bound, so self does not appear in the callback type.

var parser = Parser(source)
let read_next:func() Token = parser.next_token
let first = read_next()

The receiver is evaluated once during extraction. A mutable structure method requires a stable mutable receiver, then writes to the same location on every call. The expected type selects an overload and may omit trailing parameters that have a default value.

For a structure, the bound method keeps its receiver borrowed. A read method prevents its modification; a mutable method reserves it exclusively. Use an anonymous scope when the receiver must be reused afterward:

var counter = Counter(value:0)
{
    let increment:func() = counter.increment
    increment()
}
print(counter.value)

A method bound to an owned class retains its object identity. It can be returned or stored beyond the scope that created it. Reassigning the original variable does not change its receiver.

class Counter {
    var value:int = 0
    func add(amount:int) int { self.value += amount; return self.value }
}

func make_counter() func(int) int {
    var counter = Counter()
    return counter.add
}

func main() {
    let add = make_counter()
    print(add(2)) // 2
    print(add(3)) // 5
}

Ordinary callback copies share their receiver; copy detaches its object graph. The last reference releases the receiver, and the collector also handles cycles through callbacks. Overloads and virtual dispatch follow ordinary method call rules. A method extracted through @Class or &Class still carries that reference's lexical borrow.

If the receiver reaches a nocopy class, the copy operation fails at runtime.

Write an anonymous function

let found = any<int>([-1, 2], func(value:@int) bool {
    return value > 0
})

Omit the return type when the anonymous function returns void:

func visit(value:int, callback:func(int)) { callback(value) }
visit(42, func(value:int) { print(value) })

Capture an outer binding

An anonymous function captures only the outer bindings it uses. A captured var remains shared: modifying it inside the function modifies the surrounding variable, and copies of the function value refer to the same binding. A captured let remains immutable.

var count = 0
var increment = func() { count += 1 }
var same_increment = increment

increment()
same_increment()
print(count) // 2

A nested anonymous function may capture a binding from any lexical parent level; intermediate levels carry that context automatically.

Anonymous captures and methods bound to structures are lexical borrows. They do not copy the captured value or extend its lifetime. A function with captures, or a method bound to a local structure, can be passed to a synchronous call but cannot be returned outside the scope that owns its borrows.

Function values are language values. They expose neither a machine address nor a platform calling convention.

Back to functions · Understand borrows