I am bit confused by your package: Can’t you do the exact same thing without macros and just a slight adjustment of the project’s structure?
Usually how packages are structured is like this:
api.jl (basically identical to yours)
module Api
export Interface, foo
abstract type Interface end
# slightly more idiomatic: just declare the function and
# let Julia raise a MethodError if an implementation is missing
# Sidenote: Good style is to put your docstring here
"""
foo(interface, s, n)
Foo your interface given a String s and an Int n.
"""
function foo end
end
moduleA.jl
module ModuleA
using ..Api # note the additional .
export A # don't reexport foo or Interface
struct A <: Interface end
# attach method to Api's foo function
# note we have to qualify the function's name with the module
# because we used `using .Api`
function Api.foo(i::A, s::String, n::Int)
println("A::echo(s=$s, n=$n)")
end
end
moduleB.jl (uses import and thus can leave names unqualified)
module ModuleB
import ..Api: Interface, foo
export B # don't reexport foo or Interface
struct B <: Interface end
# attach method to Api's foo function
function foo(i::B, s::String, n::Int)
println("B::echo(s=$s, n=$n)")
end
end
new main file of the package MyPackage.jl
module MyPackage
export foo, A, B
include("api.jl")
include("moduleA.jl")
include("moduleB.jl")
using .Api
using .moduleA
using .moduleB
end
Then we can do:
julia> include("MyPackage.jl")
Main.MyPackage
julia> b = MyPackage.B()
Main.MyPackage.moduleB.B()
julia> MyPackage.foo(b, "asdf", 3)
B::echo(s=asdf, n=3)
julia> a = MyPackage.A()
Main.MyPackage.moduleA.A()
julia> MyPackage.foo(a, "asdf", 3)
A::echo(s=asdf, n=3)
Maybe I missed your point though. Can you elaborate?