# Dispatch.jl - A Julia package that makes function dispatching across modules simple, flexible, and efficient

**URL:** <https://discourse.julialang.org/t/dispatch-jl-a-julia-package-that-makes-function-dispatching-across-modules-simple-flexible-and-efficient/132890>\
**Category:** Package Announcements\
**Tags:** announcement\
**Created:** [October 5, 2025, 3:09pm UTC](https://discourse.julialang.org/t/dispatch-jl-a-julia-package-that-makes-function-dispatching-across-modules-simple-flexible-and-efficient/132890 "2025-10-05T15:09:14Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![dmgcodevil](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dmgcodevil/32/208257_2.png) [@dmgcodevil](https://discourse.julialang.org/u/dmgcodevil)\
**Post date:** [October 5, 2025, 3:09pm UTC](https://discourse.julialang.org/t/dispatch-jl-a-julia-package-that-makes-function-dispatching-across-modules-simple-flexible-and-efficient/132890/1 "2025-10-05T15:09:14Z")

</div>

# dispatch-jl

[Dispatch.jl](https://github.com/dmgcodevil/dispatch-jl) is a Julia package that provides macros for dynamic and static dispatch of function calls based on the types of the input arguments. This can be useful in scenarios where multiple modules define functions with the same names, and the appropriate function needs to be called based on the types of input arguments.

# Example

[Tests](https://github.com/dmgcodevil/dispatch-jl/tree/main/test)

Imagine you have `Api` module and two implementation modules: `ModuleA` and `ModuleB`

Api:

```jl
module Api
export Interface, foo

abstract type Interface end

function foo(i::Interface, s::String, n::Int)
    error("not implemented")
end

end

```

ModuleA:

```julia-auto
module ModuleA

include("Api.jl")

using .Api

export Interface
export A, foo

struct A <: Interface end

function foo(i::A, s::String, n::Int)
    println("A::echo(s=$s, n=$n)")
end

end

```

ModuleB:

```jl
module ModuleB

include("Api.jl")

using .Api

export Interface
export B, foo

struct B <: Interface end

function foo(i::B, s::String, n::Int)
    println("B::echo(s=$s, n=$n)")
end

end

```

[Dispatch.jl](https://github.com/dmgcodevil/dispatch-jl) provides two macros:

#### @dynamic\_dispatch

The `@dynamic_dispatch` macro dynamically dispatches function calls based on the type of the first argument. Here’s how to use it:

```jl
using Dispatch
include("Api.jl")
include("A.jl")
include("B.jl")

using .Api
using .ModuleA
using .ModuleB
import .Api: foo

@dynamic_dispatch(Main.Api.foo)

foo(A(), "a", 1)
foo(B(), "b", 2)    

```

the macro will generate a code like the below:

```jl
begin
    function foo(i, s, n)
        var"#6#obj" = i
        var"#7#m" = Dispatch.parentmodule(Dispatch.typeof(var"#6#obj"))   
        return (var"#7#m").foo(i, s, n)
    end
end

```

#### @static\_dispatch

The `@static_dispatch` macro generates specialized methods for a given function and a list of argument types. Here’s how to use it:

```jl
using Dispatch
include("Api.jl")
include("A.jl")
include("B.jl")

using .Api
using .ModuleA
using .ModuleB
import .Api: foo

@static_dispatch(Main.Api.foo, [
    Main.ModuleA.A,
    Main.ModuleB.B,
])

# or relative path
# @static_dispatch(Api.foo, [
# ModuleA.A,
# ModuleB.B,
# ])

foo(A(), "a", 1)
foo(B(), "b", 2)   
    

```

the macro will generate methods for `A` and `B`, i.e.:

```jl
function foo(i::A, s::String, n::Int)
    ModuleA.foo(i, s, n)
end

function foo(i::B, s::String, n::Int)
    ModuleB.foo(i, s, n)
end

```

### Choosing Between `@dynamic_dispatch` and `@static_dispatch`

Use `@dynamic_dispatch` during development: When exploring different implementations or when the set of argument types may change during development, `@dynamic_dispatch` provides flexibility and ease of use.  
Consider switching to `@static_dispatch` for production: Once the set of argument types is stable and performance becomes a concern, consider switching to `@static_dispatch` to minimize dispatch overhead and improve performance.

---

<div class="post-metadata">

**Author:** ![abraemer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/abraemer/32/51403_2.png) [@abraemer](https://discourse.julialang.org/u/abraemer)\
**Post date:** [October 6, 2025, 5:36am UTC](https://discourse.julialang.org/t/dispatch-jl-a-julia-package-that-makes-function-dispatching-across-modules-simple-flexible-and-efficient/132890/2 "2025-10-06T05:36:11Z")

</div>

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)

```julia-auto
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`

```julia-auto
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)

```julia-auto
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`

```julia-auto
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-auto
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?

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [October 6, 2025, 6:17am UTC](https://discourse.julialang.org/t/dispatch-jl-a-julia-package-that-makes-function-dispatching-across-modules-simple-flexible-and-efficient/132890/3 "2025-10-06T06:17:26Z")

</div>

The previous comment’s refactoring implies it, but your example has 2 modules with 2 _separate_ functions of the same name, similar to how 2 classes in a OOP language may encapsulate 2 separate methods of the same name. Julia’s multiple dispatch uses the possibly multiple argument types to select one of the possibly multiple _methods_ of _one_ function, not several functions. The package makes a dispatch system closer to OOP languages, but it’s not idiomatic in Julia.
