Change in behavior when loading a module twice

I have a module Mod1 stored in a file Mod1.jl and a module Mod2 stored in a file Mod2.jl. ‘Mod1’ exports a function userfun that uses a function foo which is defined in Mod2. Mod2 also defines some new types.

module Mod1
	export userfun

	include("Mod2.jl")
	using .Mod2

	function userfun()
		foo()
	end
end
module Mod2
	export foo
	
	using InteractiveUtils # for subtypes function
	
	abstract type MyAbstract end
	struct MyStruct <: MyAbstract end
	
	function foo()
		subtypes(MyAbstract)
	end
end

If I now write in a fresh REPL

include("Mod1.jl")
using .Mod1
userfun()

I get

1-element Vector{Any}:
 Main.Mod1.Mod2.MyStruct

as expected. But if I now (say, a user accidentaly) repeat the inclusion of the module and call userfun

include("Mod1.jl")
userfun()

I get

Any[]

Seemingly MyAbstract suddenly has now subtypes. If I afterwards redo using .Mod1 I get the same empty array.

Can anyone explain why the behavior changes after I include the module twice (This is of course not the actual code, I have tried to distill the problem as much as possible)?

  1. You should use packages to load code.
  2. My suspicion is that you noe have two abstract MyAbstract types but only onr concrete MyStruct

Thank you for the response. Poitn 2. does seem to aggree with the behavior.

Is 1. a general concept: One should not use modules unless it is wrapped into a package? Does a package always ensure that the module is not evaluated twice
?

To be clear, the problem isn’t loading the module twice (i.e. running using .Mod1 twice, which would be fine) but including the file twice, which messes up the modules, don’t do it.

1 Like

To elaborate:

julia> include("Mod1.jl")
Main.Mod1

julia> using .Mod1

julia> userfun()
1-element Vector{Any}:
 Main.Mod1.Mod2.MyStruct

julia> using .Mod1

julia> userfun()
1-element Vector{Any}:
 Main.Mod1.Mod2.MyStruct

as you can see, just doing using .Mod1 twice has no effect. When you include the file the second time you missed a warning:

julia> include("Mod1.jl")
WARNING: replacing module Mod1.
Main.Mod1

julia> userfun()
Any[]

My guess is that Main.userfun is the old Mod1.userfun, which doesn’t see the old Mod1 anymore as it has been overwritten, but you should be able to call Mod1.userfun and in fact

julia> Mod1.userfun()
1-element Vector{Any}:
 Main.Mod1.Mod2.MyStruct

julia> userfun === Mod1.userfun
false

The summary is don’t include files twice, actually read the warnings, and only reload modules.

If you’re doing all of this in the REPL for quick development, you may want to look at Revise.jl instead.