Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Closures

A lambda that needs a variable from the code around it lists the variable in square brackets after sub or function. That captures it, and the lambda becomes a closure: the code plus the captured variables. Every capture states how it is held:

'' A lambda that uses variables from around it lists them in square
'' brackets, and says for each one how it is held.
dim as int32 calls = 0
dim as int32 step_ = 5

'' byref: the lambda works on the caller's variable itself.
var countCall = sub[ byref calls ]( )
    calls += 1
end sub
countCall( )
countCall( )
print "calls:"; calls

'' byval: the lambda keeps its own copy, taken when the lambda is made.
var addStep = function[ byval step_ ]( byval x as int32 ) as int32
    return x + step_
end function
var addStepNow = function[ byref step_ ]( byval x as int32 ) as int32
    return x + step_
end function

step_ = 1000
print "byval copy:"; addStep( 1 )        '' still adds 5
print "byref view:"; addStepNow( 1 )     '' adds the current 1000

'' Modes can be mixed in one list. step_ is 1000 by now, and that is
'' the value this lambda copies.
dim as int32 total = 0
var addScaled = sub[ byref total, byval step_ ]( byval x as int32 )
    total += x * step_
end sub
addScaled( 2 )
addScaled( 3 )
print "total:"; total

Output

calls: 2
byval copy: 6
byref view: 1001
total: 5000
CaptureThe closure holdsWrites inside the lambda
byref callsthe caller's variablereach the caller
byval step_its own copy, taken when the lambda expression runsstay in the copy

countCall increments the caller's calls. addStep copied step_ when its var line ran, while it was 5, so it still adds 5 after step_ becomes 1000; addStepNow holds the variable and sees 1000. addScaled mixes the two modes and copies step_ at the point it is made, by which time it is 1000.

Pitfall

A byval capture is a snapshot taken when the lambda is made, not when it is called. A closure written early and called late, such as a handler or a comparer kept for later, sees the value from when it was written. Capture byref when the lambda must see the variable as it is at each call.

There is no default mode. A capture without byval or byref is refused:

'' Every capture must say byval or byref. Does not compile.
dim as int32 count = 0
var bump = sub[ count ]( )
    count += 1
end sub
bump( )
capturemode.tiko(3,17): error TKO0413: every lambda capture needs an explicit 'byval' or 'byref'

A closure is not a procedure pointer, because a pointer has no room for the captured variables. Assigning one to a procedure-pointer type is TKO0738: no conversion between these two types. To pass a closure to a procedure, the parameter is generic, as Higher-order procedures shows.

Activities

  1. Write a closure that appends each word it is given to a ustring captured byref, and call it three times.
  2. Change addStep to capture byref and predict its output before running.