I thought I finally understood the various subtleties of scope and closure in Julia, but obviously not! Here is a self-contained MWE that demonstrates a callback that does not work and I do not know how to fix:
iter = 8
function callback(iter)
println("callback: iter= ", iter)
end
cb2() = callback(iter)
function solver(; cb=callback)
callback()
end
solver(; cb=cb2)
iter
is a variable at global scope. I get the error message when executing the last line:
ERROR: UndefVarError: iter not defined
Stacktrace:
[1] callback()
@ Main ~/src/2022/rude/giesekus/test_global.jl:30
[2] solver(; cb::Function)
@ Main ~/src/2022/rude/giesekus/test_global.jl:40
[3] top-level scope
@ ~/src/2022/rude/giesekus/test_global.jl:44
which seems rather straightforward.
I have defined the closure cb2() = callback(iter)
, which I thought would capture the variable iter
. When the callback is called from within solver
, I expected iter
inside callback
to be visible, and yet it wasn’t. What is happening? Any insight is appreciated. Thanks.
I think you want to call cb()
here? With this change, I get:
julia> solver(; cb=cb2)
callback: iter= 8
4 Likes
Thank you! I just figured it out, returned to discourse, and you beat me to it! Thank you.
cb2
is not a closure over iter
. At least not in the sense I understand the term. It merely references Main.iter
when called; it doesn’t capture it on declaration.
julia> cb()
callback: iter= 8
julia> iter = 7
7
julia> cb()
callback: iter= 7
In constrast, this will produce a closure.
julia> function makecb(iter)
() -> callback(iter)
end
makecb (generic function with 1 method)
julia> cb_closure = makecb(iter)
#3 (generic function with 1 method)
julia> cb_closure()
callback: iter= 7
julia> iter = 6
6
julia> cb_closure()
callback: iter= 7
Observe that cb_closure
has a field iter
in contrast to cb2
.
julia> hasproperty(cb_closure, :iter)
true
julia> cb_closure.iter
7
2 Likes
Note that your original code doesn’t produce the error you reported, but “ERROR: MethodError: no method matching callback()” because of course the callback
function is defined only with one argument, there isn’t any method with zero arguments…
Noted. Thanks for the addendum.