What's up with inscrutable dispatch-related MethodErrors?

Specifically, I have defined a method like so:

    function addShape(root::CollisionNode, name::String, parent::String, objecthandle, T0)

where CollisionNode is a struct:

    struct CollisionNode
       name::String
       objecthandle::Tuple
       T::Array{Float64,(2)}
       Tref::Array{Float64, (2)}
       children::Dict{String, CollisionNode}
    end

When I attempt to call the method like so:

tree = Distance.CollisionNode("root", box1, T, eye(4), Dict{String, Distance.CollisionNode}())
Distance.addShape(tree, "childbox", "root", box2, T)

I get the following error:

ERROR: MethodError: no method matching addShape(::Distance.CollisionNode, ::String, ::String, ::Tuple{Ptr{Void},Ptr{Void}}, ::Array{Int64,2})
Closest candidates are:
  addShape(::Distance.CollisionNode, ::String, ::String, ::Any, ::Any)

which appears to be telling me that the closest candidate for a dispatch is in fact the method signature I want and have specified.

I seem to get errors of this flavor quite a lot, and although eventually I fix them I don’t understand the difference between a working and a non-working example. I just play around with the code long enough that they go away.

Clearly I’m missing something fundamental. But what?

Hm, this looks strange to me and I am not able to duplicate the error. I suspect there is something fishy going on with the modules here. When you defined addShape, did you do it inside of the Distance module?

My best guess about what’s going on is that Distance already contains an addShape method, but you are defining another addShape outside of Distance. If this is the case, the addShape outside distance is a new function. Therefore, since you are explicitly calling addShape from Distance, you get a method error which winds up with a rather wonky message since you now have multiple different functions called addShape. If this is the case, probably what you wanted to do was

import Distance.addshape
function Distance.addShape(args...)
    # stuff goes here
end

Again, this is all speculation since I don’t know the context.

I did in fact define addShape(…) inside a Module file, which I include() from the command line.

I quit the Julia REPL, restarted, and ran the code again, and the error goes away.

What just happened? At no point do I think I ever defined an addShape() method outside of the function - although I did redefine it in the Module code and re-included the code multiple times.

I have a guess: you probably did something like:

tree = Distance.CollisionNode(...)

then you reloaded the module:

include("distance.jl")

then tried to do:

Distance.addShape(tree, "childbox", "root", box2, T)

The problem is that when you re-include the module file, you create a brand-new Distance module, and brand new types within that module. So any objects that exist from before you did include() have a type belonging to the old module. Those objects thus can’t be passed into methods from the new module (since their type is different), but the error messages you get are very unhelpful because the names of the types are the same. For example:

julia> module Foo
       struct X end
       f(::X) = 1
       end
Foo

julia> x = Foo.X()
Foo.X()

julia> Foo.f(x)
1

julia> module Foo
       struct X end
       f(::X) = 1
       end
WARNING: replacing module Foo
Foo

julia> Foo.f(x)
ERROR: MethodError: no method matching f(::Foo.X)
Closest candidates are:
  f(::Foo.X) at REPL[7]:3
2 Likes

OK, I did in fact do that.

I’m developing this module. I need to make changes to the code and re-import those changes to the REPL. What’s the appropriate way of doing that? Surely quitting the REPL and reloading every time isn’t right…

Or we can be more explicit by holding onto a reference to the old module, just to show what’s going on:

julia> module Foo
              struct X end
              f(::X) = 1
              end
Foo

julia> x = Foo.X()
Foo.X()

julia> Foo.f(x)
1

julia> OldFoo = Foo
Foo

julia> module Foo
              struct X end
              f(::X) = 1
              end
WARNING: replacing module Foo
Foo

julia> Foo.f(x)
ERROR: MethodError: no method matching f(::Foo.X)
Closest candidates are:
  f(::Foo.X) at REPL[5]:3

julia> x isa Foo.X
false

julia> x isa OldFoo.X
true

Just make sure you re-construct any objects whose types are defined in the module after you re-include the module code. So, do:

include("mymodule.jl")
x = MyModule.X()
MyModule.f(x)

instead of

x = MyModule.X()
include("mymodule.jl")
MyModule.foo(x)

Or, for a more magical solution, check out https://github.com/timholy/Revise.jl

2 Likes