Jordi Manyer Fuertes
Jordi Manyer Fuertes

Reputation: 149

Using Julia abstract types in other modules

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

Answers (2)

Antonello
Antonello

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

cbk
cbk

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

Related Questions