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.