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
Abstract types allow the construction of a hierarchy of types, providing a context into which concrete types can fit. This allows you, for example, to easily program to any type that is an integer, without restricting an algorithm to a specific type of integer.
Method TablesEvery function in Julia is a generic function. A generic function is conceptually a single function, but consists of many definitions, or methods. The methods of a generic function are stored in a method table.
type and immutable are valid up to julia 0.6, mutable struct and struct are the names of the same objects in julia 0.6 and forward. mutable in mutable struct means that the fields can change - which is actually fairly rarely used so being immutable is the default. mutable struct 's are slower than struct s.
Julia is not an object-oriented language in the traditional sense in that there is no inheritance of structure. If multiple types need to share structure, you have several options: Write out the common fields manually.
Julia also has a predefined abstract "bottom" type, at the nadir of the type graph, which is written as Union {}. It is the exact opposite of Any: no object is an instance of Union {} and all types are supertypes of Union {}. Let's consider some of the abstract types that make up Julia's numerical hierarchy:
This kind of definition of function behavior by dispatch is quite common – idiomatic, even – in Julia. Method type parameters are not restricted to being used as the types of arguments: they can be used anywhere a value would be in the signature of the function or body of the function.
They have names. They have explicitly declared supertypes. They may have parameters. Because of these shared properties, these types are internally represented as instances of the same concept, DataType, which is the type of any of these types: julia> typeof (Real) DataType julia> typeof (Int) DataType
Type-annotating variables in Julia can be done using the ::operator. This operator confirms that the value on the left is the same type as the value on the right. Syntax: <expr>::<type> where, expr:It can be an expression for computing a value or an assignment statement for a variable or an declaration of a variable.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With