Before coming to the point, for your amusement I’ll share a well known footgun I er… stumbled across. A simplified version.
using .Threads
function boxingday(n)
result = sum(fetch.([
@spawn begin
y = i^2
tmp = mod(y, 7)
Libc.systemsleep(1e-8rand()) # do some more work
return tmp
end for i in 1:n]))
# a few weeks later we add some more computation...
z = n^2; tmp = (z+1)^2; r2 = mod(tmp, 7)
# done! It starts acting up!
return result, r2
end
for _ in 1:10
println(boxingday(100))
end
It prints a variety of results. If running the result = ...
part in the REPL, with n=100
, it’s always 201
as it should. The problem is that in the function, the tmp
variable has been captured and boxed, creating a race.
Anyway, this boils down to some big questions, iterated and reiterated many times: When should something be boxed, what should be captured?
When creating an anonymous function, a functor is created, with the captured variables in a struct, possibly boxed. This is easy and fast to use. But there is no way to specify which variables should be captured (and possibly boxed). The boxing decision is even done early, at a syntactic level.
For the more cautious programmer it would be nice to have a way to specify which variables to capture for an anonymous function. This would ensure that things are not captured/boxed by accident. E.g. something like
f = (a,b)(x,y) -> a*x + b*y
creating a function of x
and y
, with a
and b
captured. A Tuple
functor. It is already parsed as a function, albeit with an illegal argument name, and is reminiscent of functor definitions (ab::MyStruct)(x,y) = ab.a*x + ab.b*x
.
Would this make sense? Any thoughts?