Initializing types across modules does not preserve original type

Hello all!
Many thanks in advance for all your answers. I am a bit confused with the types defined in a module.
I am constructing two modules A and B, each of which has two defined types, say Type A and Type B, respectively. Initialization of Type A requires type B. Here is a simple code to generate it

the file “TestModuleA.jl” (edited…)

module A
 include("TestModuleB.jl")
    type TypeA
    b::B.TypeB
        function TypeA(args...)
            obj = new(B.TypeB())
            # assuming args would contain name-value argument pair
            length(args)>0? setfield!(obj,Symbol(args[1]),args[2]):nothing
            return obj                
        end
    end
end

and the file “TestModuleB.jl”

module B

type TypeB
    b::Int64
    function TypeB(args...)
      new(0)
    end
end

end

Now, in the REPL

Main> include("TestModuleA.jl"), include("TestModuleB.jl")

Initialization of A goes smoothly with default field value

Trying to initialize a new Type A, with say new field of TypeB.b=9 (instead of the default 0)

Main> A.TypeA("b",B.TypeB(9))

I get the error

ERROR: TypeError: setfield!: expected A.B.TypeB, got B.TypeB

however,

Main> typeof(B.TypeB(9))
B.TypeB

as expected.

Why is the type of TypeB changed to A.B.TypeB? why is it not the expected B.TypeB?

as I wrote the lines above I discovered that

Main> A.TypeA("b",A.B.TypeB(9))

would do the job, however, the syntax is a bit awkward, specifically if I want to initialize a new TypeA by a Type B created by a third module, or another piece of code.

many thanks!

You’ve actually created two copies of module B, but it’s difficult to tell because they have the same name. What you’ve done is equivalent to:

module A
  module B
    struct TypeB
    end
  end
end

module B
  struct TypeB
  end
end

from which it should be pretty clear that there are two module Bs, and thus an A.B.TypeB is a different type than a B.TypeB.

This is easy to fix. For example, you can just include("TestModuleA.jl") at the REPL, and then, if you want to avoid saying A.B.TypeB(), you can do:

include("TestModuleA.jl")
import A.B
B.TypeB(9)

to avoid creating a duplicate module B.

3 Likes

Thanks @rdeits !
I see, but I’m not sure I completely follow the logic. The type of the field b in TypeA is b::B.TypeB
when I initialize TypeA with a B.TypeB created outside of module A I get an error, which means that the field b is actually A.B.TypeB.
Intuitively I thought it should be the same as

module A
    type TypeA
    b::Array{Float64,2}
        function TypeA(args...)
            obj = new(zeros(1,2))
            # assuming args would contain name-value argument pair
            length(args)>0? setfield!(obj,Symbol(args[1]),args[2]):nothing
            return obj                
        end
    end
end

in which the field b is not of type A.Array{Float64,2}, or is it? sorry if the question sounds trivial.

how can I then initialize a type in module A using other types which should be ‘unaware’ of the existence of module A? or maybe Types is not the way to go?
I want to have a context-dependent type, I can use with other modules and change its values throughout simulations.

thanks!

If you use Julia’s built-in import/using mechanisms, then you won’t have to worry about duplicate module definitions (because import and using are smart enough not to generate duplicate modules when you run import B twice). All you need to do is rename your files to match the names of their modules (so, A.jl and B.jl) and add their path to your LOAD_PATH.

Then you can simplify your code to:

A.jl:

module A
  import B
  struct TypeA
    b::B.TypeB
  end
end

B.jl:

module B
  struct TypeB
  end
end

and use it like this:

push!(LOAD_PATH, "/path/to/your/modules")
import A  # automatically searches LOAD_PATH for A.jl or A/src/A.jl
import B
A.TypeA(B.TypeB())
1 Like

Brilliant, thanks @rdeits!