Home > database >  Define default method on abstract type in Julia
Define default method on abstract type in Julia

Time:11-01

How can I provide a default implementation of a method in Julia in a separate module? If the abstract type lives in the same module I have no problem, for example, this works just as I expect:

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

However, once I put the abstract type in its own module, it no longer works:

In src/qux.jl, I put:

module qux

abstract type Foo end

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

export Foo, howdy
end # module

and then in reproduce.jl I put:

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

Then I get:

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

CodePudding user response:

Your problem is explained here in the Julia manual.

The issue is that in your code, as you can see here:

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

You have two distinct howdy functions each having one method. One is deifned in qux module, and the other in the Main module.

What you want to do is to add a method to howdy function defined in qux module. I would typically do it by qualifying the exported function name with module name, as this is a clear way to signal what you want to do:

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

as you can see now you have a single howdy function having two methods and all will work as you want.

  • Related