在其他模块中使用 Julia 抽象类型

Using Julia abstract types in other modules

我正在尝试在 Julia 中使用抽象类型,遇到了一些我自己似乎无法解决的问题。

假设我在不同的文件中有 2 个 Julia 模块和一个脚本(或 REPL),它们看起来像这样:

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

我希望 f2() 可以工作,但由于某些原因它没有。从 REPL 执行时,returns 出现以下错误:

ERROR: LoadError: MethodError: Cannot `convert` an object of type Thing to an object of type AbstractThing

它与范围有关,因为 f1()f3() 都可以工作,但我无法真正弄清楚哪里出了问题或我应该如何解决它。

谁能解释一下类型作用域在这种情况下是如何工作的以及如何解决它?为什么 Julia 将 Main.Mod2.Mod1.AbstractThingMain.Mod1.AbstractThing 视为不同的类型?我该如何解决这个问题?


更新:

经过更多测试,我认为问题在于 Julia 解释器无法识别 Mod1 和 Mod2.Mod1 实际上是同一个模块。以下是我从 REPL 获得的一些输出,它们让我这样想:

julia> AbstractThing === Mod1.AbstractThing
true

julia> AbstractThing === Mod2.Mod1.AbstractThing
false

julia> Thing <: Mod1.AbstractThing
false

julia> Thing <: Mod2.Mod1.AbstractThing
true

所以它肯定是在查看 AbstractThing 的两个定义并认为它们是不同的。我不知道如何解决这个问题,但恐怕重组我的文件不是一个选择。我希望不同的人能够基于随后在 Mod1.

中识别的模板来实现他们自己的 Thing 结构

可以使 Mod1 成为一个实际的包(而不仅仅是一个本地模块)吗?

您必须导入 AbstractThing 才能扩展它。尝试添加一行

import Mod1.AbstractThing

Mod2

的定义中 using Mod1 行之后

正如@pfitzseb 对您的评论,您需要重构代码以避免两次包含一个文件。 以下是一个有效的示例。另见我的 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()