Reputation: 149
I am trying to work with abstract types in Julia, and am encountering some problems which I can't seem to figure out on my own.
Let's say I have 2 Julia modules and a script (or REPL) in different files such that they look like so:
------------------------------------- File 1
module Mod1
export AbstractThing
abstract type AbstractThing end
end
------------------------------------- File 2
module Mod2
export Thing
include("path/to/File1")
using Mod1
struct Thing <: AbstractThing
...
end
function f1()
v :: Vector{AbstractThing} = []
push!(v,Thing())
end
end
------------------------------------- File 3
include("path/to/File1")
using Mod1
include("path/to/File2")
using Mod2
function f2()
v :: Vector{AbstractThing} = []
push!(v,Thing())
end
function f3()
v :: Vector{Main.Mod2.Mod1.AbstractThing} = []
push!(v,Thing())
end
What I would like is for f2()
to work, but for some reason it does not. When executed from the REPL, it returns the following error:
ERROR: LoadError: MethodError: Cannot `convert` an object of type Thing to an object of type AbstractThing
It has something to do with scope, because both f1()
and f3()
work, but I can't really figure out what is wrong or what I should do to fix it.
Can someone explain how type scope works in this sort of context and how to fix it? Why is Julia seeing Main.Mod2.Mod1.AbstractThing
and Main.Mod1.AbstractThing
as separate types? How can I fix this?
Update:
After some more testing, I think the problem is that the Julia interpreter does not recognise that Mod1 and Mod2.Mod1 are actually the same module. Here are some of the outputs I got from the REPL that make me think this:
julia> AbstractThing === Mod1.AbstractThing
true
julia> AbstractThing === Mod2.Mod1.AbstractThing
false
julia> Thing <: Mod1.AbstractThing
false
julia> Thing <: Mod2.Mod1.AbstractThing
true
So it is definitely looking at the two definitions of AbstractThing
and considering them as different. How can this be solved I do not know, but restructuring my files is not an option I am afraid. I want different people to be able to implement their own Thing
struct, based on a template that is then recognised in Mod1
.
Could making Mod1
an actual package (and not simply a local module) work?
Upvotes: 3
Views: 559
Reputation: 6431
As @pfitzseb commented to you, you need to restructure your code in order to avoid including a file twice. The following is an example that works. See also my tutorial on packages or modules (also available as YouTube video):
------------------------------------- file1.jl:
module Mod1
export AbstractThing
abstract type AbstractThing end
end
------------------------------------- file2.jl:
module Mod2
export Thing, f1
using ..Mod1 # You need to "go up" one level to main and then "go down" one level to Mod1
struct Thing <: AbstractThing
end
function f1()
v :: Vector{AbstractThing} = []
push!(v,Thing())
end
end
------------------------------------- file3.jl:
include("file1.jl") # Do all the "includes" at the beginning of your project..
include("file2.jl")
using .Mod1 # Here you are in Main, you need to go down one level to Mod1..
using .Mod2 # Same here to Mod2
function f2()
v :: Vector{AbstractThing} = []
push!(v,Thing())
end
function f3()
v :: Vector{Main.Mod2.Mod1.AbstractThing} = []
push!(v,Thing())
end
f1()
f2()
f3()
Upvotes: 3
Reputation: 4370
You have to import AbstractThing
to be able to extend it. Try adding a line
import Mod1.AbstractThing
just after the line with using Mod1
in your definition of Mod2
Upvotes: 0