Определите метод по умолчанию для абстрактного типа в Julia

#inheritance #module #julia

Вопрос:

Как я могу предоставить реализацию метода по умолчанию в Julia в отдельном модуле? Если абстрактный тип живет в том же модуле, у меня нет проблем, например, это работает так, как я ожидаю:

 abstract type Foo end

howdy(x::Foo)::Union{String, Nothing} = nothing

struct Bar <: Foo end

function howdy(x::Bar)::Union{String, Nothing}
    "I'm a Bar!"
end

struct Baz <: Foo end

if abspath(PROGRAM_FILE) == @__FILE__
    bar = Bar()
    s = howdy(bar)
    if isa(s, String)
        println(s)
    end
    baz = Baz()
    t = howdy(baz)
    if isa(t, String)
        println(t)
    end
end
 

Однако, как только я помещаю абстрактный тип в его собственный модуль, он больше не работает:

В src/qux.jl , я вставил:

 module qux

abstract type Foo end

howdy(x::Foo)::Union{String, Nothing} = nothing

export Foo, howdy
end # module
 

а потом reproduce.jl я вставил:

 using qux

struct Bar <: Foo end

function howdy(x::Bar)::Union{String, Nothing}
    "I'm a Bar!"
end

struct Baz <: Foo end

if abspath(PROGRAM_FILE) == @__FILE__
    bar = Bar()
    s = howdy(bar)
    if isa(s, String)
        println(s)
    end
    baz = Baz()
    t = howdy(baz)
    if isa(t, String)
        println(t)
    end
end
 

Тогда я получаю:

 julia --project=. reproduce.jl
I'm a Bar!
ERROR: LoadError: MethodError: no method matching howdy(::Baz)
Closest candidates are:
  howdy(::Bar) at ~/qux/reproduce.jl:5
Stacktrace:
 [1] top-level scope
   @ ~/qux/reproduce.jl:18
in expression starting at ~/qux/reproduce.jl:11
 

Ответ №1:

Ваша проблема описана здесь, в руководстве Julia.

Проблема в том, что в вашем коде, как вы можете видеть здесь:

 julia> module qux

       abstract type Foo end

       howdy(x::Foo)::Union{String, Nothing} = nothing

       export Foo, howdy
       end # module
Main.qux

julia> using .qux

julia> struct Bar <: Foo end

julia> function howdy(x::Bar)::Union{String, Nothing}
           "I'm a Bar!"
       end
howdy (generic function with 1 method)

julia> methods(howdy)
# 1 method for generic function "howdy":
[1] howdy(x::Bar) in Main at REPL[4]:1

julia> methods(qux.howdy)
# 1 method for generic function "howdy":
[1] howdy(x::Foo) in Main.qux at REPL[1]:5
 

У вас есть две различные howdy функции, каждая из которых имеет один метод. Один определен в qux модуле, а другой-в Main модуле.

Что вы хотите сделать, так это добавить метод к howdy функции, определенной в qux модуле. Обычно я бы сделал это, указав имя экспортируемой функции с именем модуля, так как это четкий способ указать, что вы хотите сделать:

 julia> module qux

       abstract type Foo end

       howdy(x::Foo)::Union{String, Nothing} = nothing

       export Foo, howdy
       end # module
Main.qux

julia> using .qux

julia> struct Bar <: Foo end

julia> function qux.howdy(x::Bar)::Union{String, Nothing}
           "I'm a Bar!"
       end

julia> methods(qux)
# 0 methods:

julia> methods(howdy)
# 2 methods for generic function "howdy":
[1] howdy(x::Bar) in Main at REPL[4]:1
[2] howdy(x::Foo) in Main.qux at REPL[1]:5
 

как вы можете видеть теперь, у вас есть одна howdy функция, имеющая два метода, и все они будут работать так, как вы хотите.