Why doesn't method dispatch work on the same candidate included from different sources?

I originally made the question on SO but nobody could help.

I have this error:


ERROR: MethodError: no method matching 
correct_field(::Main.Poetry.LoadTemplates.TicketTemplates.myStruct, ::Dict{String, Any})
Closest candidates are:
  correct_field(::Main.Poetry.ContentGen.TicketTemplates.myStruct, ::Any)
  (...)

The problem is that I loaded the structs in LoadTemplates and now I am using a generic function from ContentGen that dispatches on those structs.

How is this usually solved? I guess I can “unmodulate” my Julia files. Or maybe there’s a way to chop off all those Main.Poetry... to get just TicketTemplates.myStruct.

Finally, I’d like to know what’s behind this design decision and what are the use cases.

If you check the types of your objects, you’ll notice that they have different module hierarchies:

::Main.Poetry.LoadTemplates.TicketTemplates.myStruct
::Main.Poetry.LoadTemplates.ContentGen.myStruct

So the two structs are not the same. I’m guessing you includeed a file defining that struct twice, once in TicketTemplates and once in ContentGen? If so, these two definitions define distinct types, due to being in a different namespace due to being in a different module.

2 Likes

Yes, that’s what I say in my question.

What should I do about it?

Don’t just include the struct definition twice and instead import the binding via using or import from the module.

https://docs.julialang.org/en/v1/manual/modules/#Submodules-and-relative-paths

Damn I spent so much time figuring out how to properly import local modules. I wound up with include followed by a using .MyModule.

But I think I get it now!
I have to include before the module begins and then do using inside it.

Yes. using/import are for navigating the module hierarchy. include is for establishing that hierarchy (it’s literally pasting the code from the included file where you wrote include). Thus, multiple includes literally create new nodes in the module hierarchy - they are not linked to the original file.

1 Like

This feels unfair @Sukera :frowning:

ERROR: MethodError: no method matching correct_field(::Main.TicketTemplates.ticketSN, ::Dict{String, Any})
Closest candidates are:
  correct_field(::Main.TicketTemplates.ticketSN, ::Any)

What am I missing now?

That’s odd :thinking: How are you defining those methods & that struct? Do you have a MWE?

I’m not sure what’s a MWE I tried googling it but to no avail, that should answer your question :smiley:

A minimal reproducible example follows.

File structs.jl:

module Structs
export myStruct

struct myStruct
end

end

File load.jl:

include("structs.jl")
module Load
using Main.Structs
export load_struct

function load_struct()
    myStruct()
end

end

File methods.jl:

include("structs.jl")

module Methods
using Main.Structs
export method
function method(strt::myStruct)
    "I got called"
end
end

File poet.jl:

include("methods.jl")
include("load.jl")
module Poet
using Main.Methods
using Main.Load

strt = load_struct()
method(strt)
end

Running poet.jl yields:

ERROR: MethodError: no method matching method(::Main.Structs.myStruct)
Closest candidates are:
  method(::Main.Structs.myStruct) 

It’s a (M)inimal (W)orking (E)xample.

You’re still including that file twice, which will still create two modules. The first module will be the one Load sees and binds to, then in methods.jl you create a new Structs module which is what Methods sees and binds to (and which overwrites the binding created in load.jl).

I don’t know why you’re creating submodules for the structs in particular, I’d just have them defined like so:

poet.jl
module Poet
include("structs.jl")
include("methods.jl")
end
structs.jl
struct myStruct
  ...
end
modules.jl
function method(strt::myStruct)
   "I got called"
end

Or, if you insist on the module hierarchy:

poet.jl
module Poet
include("structs.jl")
include("methods.jl")
end
structs.jl
module Structs

export myStruct

struct myStruct
  ...
end

end
modules.jl
module Methods

using ..Structs

function method(strt::myStruct)
   "I got called"
end

end

Ah that was funny, a MWE is a MRE.

I thought modules would have equivalent functionality to C’s header guards/#pragma once. Hmm.

Putting all includes on poet.jl solved the issue. But I find it weird, given that this makes the modules dependent on the client which uses them…

They are not - they are distinct objects that you can manipulate.

Sounds like there’s more to your motivation than just the include stuff? Do you have more context, maybe we’re encountering an XY-Problem?

1 Like

Thank you for introducing me to the XY-problem name. I knew it but didn’t know it was coined as such

That’s a very real possibility. I am writing a fuzz tester for a C program. I looked around for design patterns but didn’t find any so my current implementation feels sketchy to me.

The fuzz tester simply generates txt files following templates and I vary the fields depending if they are evaluated in branching conditions or not, so that I can better cover the code flow.

Eventually I might drop the structs.jl file for json. But then I’d have to find a better way to match the methods of generic functions.

Maybe I could simply do away with all modules, but that would lead me to my initial annoyance of struct redefinition error

I’m not sure I follow. Do you have a small pseudocode example of what you’re trying to do?

In what context do you get that error?

I’m developing my code in vscode shell so whenever I rerun code that redefines the structs.

I just follow the advice on this post: workspace() is gone

I still have some reading to do on Julia workflow.