Multimodule project: Error when calling function from imported module in Julia

I’m new to Julia. I want to organize my code into modules. I have three modules:

ModuleA.jl :

module ModuleA

include("ModuleB.jl")
export process
using .ModuleB
function process(foo::Foo)
end

end

ModuleB.jl :

module ModuleB
export Foo

struct Foo end

end

ModuleC.jl:

include("ModuleA.jl")
include("ModuleB.jl")

using .ModuleA
using .ModuleB

process(Foo())

When I run julia ModuleC.jl I get the following error:

ERROR: LoadError: MethodError: no method matching process(::Foo)

Closest candidates are:
  process(::Main.ModuleA.ModuleB.Foo)
   @ Main.ModuleA /julia/mutli-module/ModuleA.jl:6

What I’m doing wrong?

1 Like

Hi!

You are including two copies of ModuleB when one would be enough, e.g. like so:

module ModuleA

include("ModuleB.jl")
export process

# change: we want to re-export `Foo` 
export Foo

using .ModuleB
function process(foo::Foo)
end

end

(ModuleB.jl stays the same)

include("ModuleA.jl")

# change: no include or using needed

using .ModuleA

process(Foo())

What happens is that there are basically two versions of Foo in your original code (note that include just pastes the file content at that position). One is Main.ModuleA.ModuleB.Foo and one is Main.ModuleB.Foo which exists, because there is another module B in your ModuleC.jl from the second include("ModuleB.jl").

Since there is an export Foo in ModuleB but not from ModuleA, whenever you use the name Foo in the main Module, it will be Main.ModuleB.Foo. If you try to add export Foo just to ModuleA.jl without removing the second include and using .ModuleB, you will also get a warning about two objects with the same name – so you cannot use Foo in this case at all.

The function in ModuleA uses Foo from the “inner” ModuleB, i.e. Main.ModuleA.ModuleB.Foo hence you get a method error if you try to call it with Foo from the top-level module.

In principle, the two structs in the two copies of ModuleB are different things that could also have differend code and just happen to have the same names. You only need one copy of the code of ModuleB (i.e. only one include) and then you can propagate the names as you like.


Another option would be to also export the whole ModuleB from ModuleA

module ModuleA

include("ModuleB.jl")
export process

export ModuleB

using .ModuleB
function process(foo::Foo)
end

end

Then you can do

include("ModuleA.jl")
# just remove the second `include`

using .ModuleA
using .ModuleB

process(Foo())

If there are many things from ModuleB you want to pass on through ModuleA, perhaps Reexport.jl is also useful.

1 Like