I would like to have a closure modify a captured variable. Here is a simple example:
function fancy_iteration(f::F, itr) where {F}
for pt in itr
f(pt)
end
end
function mysum(f, itr)
sum = 0.
fancy_iteration(itr) do pt
sum += f(pt)
end
sum
end
@code_warntype mysum(sin, ones(3))
Currently inference fails on this. Are the recommended workarounds?
julia> @noinline function call_func(f::Function)
println(f())
end
call_func (generic function with 1 method)
julia> # Simple version. `i` is a Box here.
function log_loop(N, unlikely)
s = 0
for i=1:N
s += i
if unlikely
call_func(()->"asdf $i")
end
end
end
log_loop (generic function with 1 method)
julia> @code_warntype log_loop(1, true) # fails to infer `i`
Yeah sure in this example one can even use the sum function. But the goal of the question is to be as close to the style of the example, while avoiding inference problems.
function fancy_iteration(f, itr)
for pt in itr
f(pt)
end
end
function mysum2(f, itr)
for i in 1:1
sum = Ref(0.0)
if true
fancy_iteration(itr) do pt
old = sum[]
new = old + f(pt)
sum[] = new
end
end
return sum[]
end
error()
end
@code_warntype mysum2(sin, 1:3) #fails
The traditional let-block workaround works just fine, though:
julia> function mysum2(f, itr)
for i in 1:1
sum = Ref(0.0)
if true
let sum = sum
fancy_iteration(itr) do pt
old = sum[]
new = old + f(pt)
sum[] = new
end
end
end
return sum[]
end
error()
end
You can think of the let block as a really obvious promise to the compiler that nothing inside the closure could possibly modify what sum is bound to in the outer scope.
Thanks I also just tried that! Would you say that inference does well if I use let blocks + Ref for updating captured variables or are there other traps?