I just came across a (to me, inexplicable) case of mutual infinite
recursion in my code. It was pretty well hidden among lots of cruft, so
I've tried to minimize it. The minimal example still exhibits the behavior,
but there are a couple of differences (I'll get back to some). Also, I'm
sure I could have shrunk it even more, but obvious attempts all seemed to
remove the problem (and give me a more obviously correct error message).
Anyway, here's the code:
type B
end
type X{T}
baz::Vector{T}
end
function foo(a, c)
bar(b, c)
end
function bar{T}(d::(X{T}, Int), val::T)
foo("err", d[1].baz)
end
b = B()
function test()
bar(b, "wat")
end
test()
When run, this seems to get into an infinite recursion between foo and bar,
where I would have (with this minimized version) have expected an error
message of the type
*ERROR: no method bar(B, ASCIIString)*
After all, as far as I can see, no such method exists, and the call inside
foo ought not somehow create it…?
This is what you get if you, for example, comment out the bar(b, c) call or
replace its b argument with a local B. Note that in the original, that call
did *not* (as far as I can tell) refer to a global variable in this way.
Indeed, the call was inside a loop, and its first argument was the iterator
variable. But I guess if the dispatch somehow ended up using the same
function anyway, it doesn't matter.
Also, the test part was originally in a FactCheck context environment, and
the currently global variable was in the enlosing facts environment. This
worked, but if I put the call inside a for loop (which was originally, in
fact, in a different function, in a different module), I once again got the
same problem.
So I guess I'm asking two things: First, what am I missing here? And
second, how can I more effectively debug stuff like this in Julia?-) (I.e.,
figure out why my methods are instantiated in ways I didn't intend, like
here.)