I have something like the following situation:
module Cache
export cache
function cache(data)
throw(NotImplementedException())
end
end
module FileCache
import Cache
function Cache.cache(data)
# do cache stuff
end
end
include("Cache.jl")
include("FileCache.jl")
# use caching
However, Julia complains that FileCache.cache
overwrites Cache.cache
and breaks pre-compilation. Having different method signatures fixes the problem, but I don’t want to invent artificial function signatures for interface methods just for this purpose.
What’s the correct way to do it?
1 Like
Going through some code I’ve seen from other authors, I think the solution is to pass a type for dispatching and specializing methods, ie:
module Cache
function cache()
throw(NotImplementedException())
end
end
module FileCache
function cache(::Cache{FileCache}, data)
# stuff
end
end
Is this the (only) way?
You can define a function with no methods:
function cache end
in the Cache
module.
12 Likes
Ah, that’s brilliant, thanks!
An aside, after working with a lot of code that onces used this kind of NotImplementedException
,
we concluded it was an antipattern.
and replaced it with declaring the functin in the
function cache end
form.
- Plus a docstring saying what the espected signature was.
- Plus a test-suite function which accepts an implementation of the interface in the docstring and tests that it follows the interface.
Its great.
Strong recommend,.
12 Likes
Excellent! It felt too much like Java anyway!
2 Likes
the really big downside of the NotImplementedException
is that if you do somethig like
function train(::AbstractModel, data)
throw(NotImplementedException())
end
struct GoodModel <: AbstractModel
...
end
function train(mdl::GoodModel, data::Vector)
...
end
then call
train(mdl, (1,2,3))
(a tuple rather than a vector)
then you get a NotImplementedException
with a uninformative error message.
Rather than a MethodError
listing what you called, vs what exists
4 Likes