Closures

Closures

A closure is a callable value. The type is written closure(A, B) => R, and the literal uses the closure keyword:

let handler: closure(string) => string = closure(params: string) => string {
    return `{"ok":true}`
}

Closures are used wherever a callback is needed, most visibly when registering handlers:

application.on("ide.fs.tree", closure(params: string) => string {
    let parsed: json::JsonValue = json::parse(params)
    if parsed.kind() != "object" {
        return `{"ok":false}`
    }
    return `{"ok":true}`
})

Captures

A closure captures the variables it uses. When it needs a value from the enclosing scope that outlives the current frame, the capture list makes that explicit:

application.on("ide.agent.start", closure[application](params: string) => string {
    application.emit("ide.agent:event", `{"t":"log","text":"started"}`)
    return `{"ok":true}`
})

The names in brackets after closure are captured by the closure and stay reachable for as long as it does. Without the capture, a variable that goes out of scope before the closure runs is not available to it.

Storing closures

A closure is an ordinary value, so it can be held in a field or a collection:

class Bridge {
    [private] handlers: Map<string, closure(string) => string>

    [mutates] fn on(method: string, handler: closure(string) => string) {
        this.handlers.set(method, handler)
    }
}