Often, most of the work during debugging is to reconstruct the intermediate states in an algorithm. A debugger can often facilitate this need, but it could be rather cumbersome to use if the breakpoint condition is complicated.
In Julia, we usually develop a package with a Julia REPL open on the side. All the code executed in the REPL resides in the Main
module. Hence, one can access REPL variables via Main.a_variable_name
in the package one is developing. While debugging, we need to send the intermediate states of a function to Julia REPL. Unfortunately, Julia forbids rebinding of a variable from another module. One can bypass this limitation by defining an unused variable like _a = Ref{Any}()
in the REPL. Then, modify this variable via Main._a[] = a_state_of_interest_1, a_state_of_interest_2
in some function of a package.
julia> function foo(n)
a = b = 1
for i in 1:n
a, b = b, a + b
if a <= 0
Main._a[] = a, b
end
end
return a
end
foo (generic function with 1 method)
julia> _a = Ref{Any}()
Base.RefValue{Any}(#undef)
julia> foo(100)
1298777728820984005
julia> _a
Base.RefValue{Any}((-2437933049959450366, 3736710778780434371))