Initializing types across modules does not preserve original type


#1

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!


#2

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

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!


#4

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())

#5

Brilliant, thanks @rdeits!