Interesting question. The closest I could get was:
fref = Ref{Function}()
fref[] = ()->begin
println("foo")
sleep(1)
end
function outer()
for i in 1:20
fref[]()
println(i)
end
end
# run this, then redefine the function in `fref`
t = @async outer()
# run this if you want to see errors thrown by the task
wait(t)
But when you try to redefine the function you get a world-age error because outer is older than the function in fref. Generally in Julia when a func1 calls func2, then func2 gets redefined, Julia will recompile func1 so that it’s up to date. The world age stuff is the mechanism to make sure that those dependencies are updated as needed.
It would be more idiomatic to make the function behavior conditional on something that you can change — can be a parameter, a shared variable (if you are using threads), or something you read from a file. The implementation of how you propose to do
is crucial. Also, perhaps you could give more context: what is it you are trying to do?
One way around it is to use Base.invokelatest to call the newest definition.
help?> Base.invokelatest
invokelatest(f, args...; kwargs...)
Calls f(args...; kwargs...), but guarantees that the most recent method of f
will be executed. This is useful in specialized circumstances, e.g.
long-running event loops or callback functions that may call obsolete
versions of a function f. (The drawback is that invokelatest is somewhat
slower than calling f directly, and the type of the result cannot be
inferred by the compiler.)
The performance of the function call is a bit slower though.
Ah yes, good idea. So the full working example looks like:
function inner()
println("foo")
sleep(1)
end
function outer()
for i in 1:20
Base.invokelatest(inner)
println(i)
end
end
t = @async outer()
# call this to get any errors thrown
wait(t)
Hi Tamas,
my question didn’t come from nowhere and had the specific purpose of comparing the level of interactivity provided by Julia’s REPL with respect to what is provided by other “lisp languages”.
The “omy” scenario is highly typical of Common Lisp and it’s also supported by some scheme implementations (i.e. Gambit).
The best answers were by Chakravala and Ssfrr. Given that both of them were able to jump straight to the point and address the matter about late binding I believe that my question had more than enough context at least for them.
Thanks,
Regards,
Luca
Have just tried it both in Atom and in a bare REPL. It doesn’t seem to work. I am changing “foo” to “hello” and evaluating again inner without any apparent effect.
From your tone it is my impression that you may think I asked for something unreasonable.
Note however that @chakravala’s answer also points out that you will pay a performance penalty for invokelatest, as it does not mesh well with Julia’s performance model.
To give a CL example, this is a bit comparable to using cl:eval heavily: while technically you can, it is not idiomatic and you usually run into issues.
This can be easily illustrated by modifying the example above to use the result of inner, eg look at
inner(i) = (sleep(1); i)
outer(n) = sum([Base.invokelatest(inner, i) for i in 1:n])
@code_warntype outer(3)
@Tamas_Papp gave the most useful answer IMO. While it’s possible to do what you’re asking, it sounds like a thoroughly bad idea (at least in Julia), that is destined for code that’s hard to understand, bugs, and problems down the road.