Using modules causes isa() to return the incorrect result

# GameObj.jl
module GameObj
export GameObject
abstract type GameObject end
end
# WeaponObj.jl
module WeaponObj
export Weapon
include("GameObj.jl")
using .GameObj
abstract type Weapon <: GameObject end
end
# ChargeGunObj.jl
module ChargeGunObj
export ChargeGun
include("WeaponObj.jl")
using .WeaponObj

struct ChargeGun <: Weapon
    number_of_charges::Int32
    damage::Int32

    function ChargeGun()
        number_of_charges = 10
        damage = 5
        new(number_of_charges,damage)
    end
end
end
using .ChargeGunObj
using .WeaponObj

function main()

    cg :: Weapon = ChargeGun()
    println(cg.damage)

end

main()

The problem is when I use Weapon as a type annotation, it returns:

LoadError: MethodError: Cannot `convert` an object of type ChargeGun 
                        to an object of type Weapon

and isa() returns the incorrect result:

cg = ChargeGun()
println(isa(cg,Weapon)) # returns false, despite the fact that ChargeGun is a Weapon.

If I were to remove the modules and write everything out in the main.jl, it works.

The problem is that you accidentally define two different modules named WeaponObj. One is Main.WeaponObj, and one is Main.ChargeGunObj.WeaponObj. Your codes give something like this tree:

Main
├── ChargeGunObj
│   ├── ChargeGun <: Main.ChargeGunObj.WeaponObj.Weapon
│   └── WeaponObj
│       └── Weapon
└── WeaponObj
    └── Weapon

where, in your main function create a Main.ChargeGunObj.ChargeGun (which is a subtype of Main.ChargeGunObj.WeaponObj.Weapon but check if it is a subtype of Main.WeaponObj.Weapon. (which, while they have the same name, are not the same type, i.e. Main.ChargeGunObj.WeaponObj.Weapon !== Main.WeaponObj.Weapon).

The rule is to only include the same file once.

I’m a little confused about how to fix it, do I remove the using .WeaponObj and just leave using .ChargeGun?

If I do that, isa() still returns false, and I can’t do cg :: Weapon = ChargeGun() because WeaponObj isn’t available.

How about something like this:

$ cat GameObj.jl 
module GameObj
    export GameObject
    abstract type GameObject end
end # module

$ cat WeaponObj.jl 
module WeaponObj
    export Weapon
    # Use .. to look for GameObj in the parent module
    # instead of including the file
    using ..GameObj
    abstract type Weapon <: GameObject end
end

$ cat ChargeGunObj.jl 
module ChargeGunObj
    export ChargeGun
    using ..WeaponObj

    struct ChargeGun <: Weapon
        number_of_charges::Int32
        damage::Int32

        function ChargeGun()
            number_of_charges = 10
            damage = 5
            new(number_of_charges, damage)
        end
    end
end

$ cat Game.jl 
include("GameObj.jl")
include("WeaponObj.jl")
include("ChargeGunObj.jl")

using .GameObj, .WeaponObj, .ChargeGunObj

function main()
    cg :: Weapon = ChargeGun()
    println(cg.damage)
end

main()

$ julia Game.jl
5