Closure
closure1 | = | [’^ ’ [‘_ ’ ws] [expression ws]] [parameters ws] code-block |
closure-tail-args2 | = | ws send-args ws closure [ws ‘; ’ ws return-args] |
invoke-class3 | = | [‘_ ’ | ‘+ ’] parameters |
A closure (also called closures in Swift and known as lambda expressions in C++ or blocks in Ruby and Smalltalk) is a series of expressions grouped together like an anonymous (without a name) routine together with parameters, that also includes (closes around) a referencing environment for the receiver (this
) and non-local variables of that code. This referencing environment creates and stores variables with the same name of any captured variables (including this
) outside its immediate lexical scope by binding a reference to the objects the captured variables refer to at the time of the evaluation of the closure expression (and not the value of the captured variables at the time of the closure’s invocation).
To get something similar to SkookumScript closures, a C++ lambda expression would use a lambda-introducer of [&, this]
if the this is captured or [&]
if a receiver expression is supplied using ^
.
Closures allow treating code as an object so it can be passed to and returned from routines and delay its evaluation until needed. This allows callbacks, custom control flow, and all manner of powerful constructs.
Most of the time the syntax for closures can be fairly straightforward, though on occasion they can have some of the most tricky syntax in the language.
!triple: (Integer x)[3 * x]
triple(4) // returns 12
The return class type of a closure can often be inferred so you don’t need to explicitly specify it.
The above triple
closure infers the Integer
return class, and it is the same as:
!triple:
(Integer x) Integer
[
3 * x
]
See some closure-related info including closure sample code on the SkookumScript forum.
Inferring type from context
The parameters for a closure can be inferred from surrounding context so they can be much more concise—see “Closures as arguments” in the Primer.
Closure tail arguments
Whenever the last parameter of a routine is a closure, the routine parentheses ( )
are not needed—the closure is used as a terminator for the arguments. This allows you to make routines that look similar to built-in language flow control commands. See “Closure tail arguments” in the Primer.
Calling closures
Learn about invoking closures at “Closure invoke operator” in the Primer.
-
Optional
^
or parameters or both must be provided (unless used in closure-tail-args where both are optional). Optional expression (which may not be code-block or closure) will be captured and used as receiver/this for the code-block – if not providedthis
is inferred. Optional_
indicates it is durational (like a coroutine) – if not present durational/immediate inferred via code-block. Parameter types, return type, scope, whether surroundingthis
or temporary/parameter variables are used and captured may all be inferred if omitted. ↩ -
Routines with last send parameter as mandatory closure may omit brackets
()
and closure arguments may be simple code-block (omitting^
and parameters and inferring from argument parameter context). Default arguments indicated via comma,
separators. ↩ -
_
indicates durational (like coroutine),+
indicates durational or immediate (coroutine or method) and lack of either indicates immediate (like method). ClassClosure
matches any closure interface. Identifiers and defaults used for closure arguments without parameters. ↩