# Julia adoption: Method confusion

**URL:** <https://discourse.julialang.org/t/julia-adoption-method-confusion/128672>\
**Category:** New to Julia\
**Tags:** question\
**Created:** [May 3, 2025, 6:45pm UTC](https://discourse.julialang.org/t/julia-adoption-method-confusion/128672 "2025-05-03T18:45:42Z")\
**Posts on this page:** 13\
**Page:** 1

<div class="post-metadata">

**Author:** ![ShalokShalom](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/shalokshalom/32/52462_2.png) [@ShalokShalom](https://discourse.julialang.org/u/ShalokShalom)\
**Post date:** [May 3, 2025, 6:45pm UTC](https://discourse.julialang.org/t/julia-adoption-method-confusion/128672/1 "2025-05-03T18:45:42Z")

</div>

I have been in the ‘Programming Languages Development’ Discord (fun place) for a couple of weeks now, so many times when I bring up multiple dispatch, someone says:

“That’s messy, you quickly drown in methods, and loose sight over what signature calls what body.”

I feel like in order to help adoption, it would be nice to have some kind of reference (in the documentation) that collects all the tools and techniques, that help around that.

Currently, it seems that is still the number one concerning the people, who know at least that its different from function overloading.

I like to put together a few ideas, how you guys deal with that, and collect them on a doc page.

What do you think about it?

---

<div class="post-metadata">

**Author:** ![Gesee](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gesee/32/217235_2.png) [@Gesee](https://discourse.julialang.org/u/Gesee)\
**Post date:** [May 3, 2025, 7:14pm UTC](https://discourse.julialang.org/t/julia-adoption-method-confusion/128672/2 "2025-05-03T19:14:59Z")

</div>

For me multiple dispatch is a great thing, it allow an unique way to do OOP, plus it makes abstraction and interfacing easier while avoiding the inheritance.  
For example

```julia

abstract type CustomArray <: AbstractArray end

struct CArray <: CustomArray
data
end

# Then you create dispatch for size, IndexStyle, length, getindex, setindex! and you are pretty much done, you can use the different Arrays methods like broadcasting or iteration

```

---

<div class="post-metadata">

**Author:** ![mkitti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkitti/32/12459_2.png) [@mkitti](https://discourse.julialang.org/u/mkitti)\
**Post date:** [May 3, 2025, 7:57pm UTC](https://discourse.julialang.org/t/julia-adoption-method-confusion/128672/3 "2025-05-03T19:57:40Z")

</div>

In some ways, they are correct. It is messy and does create challenges. While Julia’s implementation is quite efficient, multiple dispatch makes us prone to inadvertent invalidations.

In practice, it is not as messy as some would imagine if we obey certain social practices such as avoiding type piracy or punning.

In many ways, I find Julia’s dispatch much more intuitive than dispatch in object oriented schemes in Java.

---

<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:** [May 4, 2025, 5:33am UTC](https://discourse.julialang.org/t/julia-adoption-method-confusion/128672/4 "2025-05-04T05:33:30Z")

</div>

With an active runtime, we can figure out what method a call signature (`which`/`@which`) or statically dispatched call (Cthulhu.jl, would be nice if there was a `@code_XX` to do a non-recursive version of that). Runtime dispatch goes to multiple methods depending on the inputs, which is the point and isn’t too different from other languages. But if we’re just looking right at source code, it’s true that a call often won’t have its input types specified (dynamic typing after all), and even if it does, method signatures often won’t obviously match on sight. Static typing with little inference or avoiding multiple methods for a function (including function overloading) does have its perks, and we do have to deal with the tradeoffs. You can help, but sometimes it’s not enough to convince people out of their valid preferences.

---

<div class="post-metadata">

**Author:** ![ShalokShalom](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/shalokshalom/32/52462_2.png) [@ShalokShalom](https://discourse.julialang.org/u/ShalokShalom)\
**Post date:** [May 5, 2025, 7:04am UTC](https://discourse.julialang.org/t/julia-adoption-method-confusion/128672/5 "2025-05-05T07:04:59Z")

</div>

This video shows many tools, that could help with that.

Julia is a language that could benefit a lot from IDE features like these.

(You could look it at 1.05x speed) [https://youtu.be/baxtyeFVn3w](https://youtu.be/baxtyeFVn3w)

---

<div class="post-metadata">

**Author:** ![DanielVandH](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/danielvandh/32/31134_2.png) [@DanielVandH](https://discourse.julialang.org/u/DanielVandH)\
**Post date:** [May 5, 2025, 7:29am UTC](https://discourse.julialang.org/t/julia-adoption-method-confusion/128672/6 "2025-05-05T07:29:36Z")

</div>

Any features in particular, without having to first go through a 43 minute video?

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [May 5, 2025, 9:23am UTC](https://discourse.julialang.org/t/julia-adoption-method-confusion/128672/7 "2025-05-05T09:23:21Z")

</div>

> [@ShalokShalom](#):
>
> you quickly drown in methods, and loose sight over what signature calls what body

The point of generic programming is precisely that: you don’t have to keep track of which particular method is being called as they should all conform to the same interface. (Debugging is of course an exception).

For `a + b`, all I need know is that the concept of “addition” makes sense, either `a` and `b` are both numbers, or the analogy can be made meaningfully (cf `0 + true`).

This becomes problematic when people try to pun on symbols. There have been discussions about Base exporting some common verbs, so that people don’t need to import a common `WhateverBase.jl` package, but these are misunderstandings of how Julia should be used.

So my advice would be: _“Don’t do this and you will be fine.”_

---

<div class="post-metadata">

**Author:** ![ShalokShalom](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/shalokshalom/32/52462_2.png) [@ShalokShalom](https://discourse.julialang.org/u/ShalokShalom)\
**Post date:** [May 7, 2025, 5:27pm UTC](https://discourse.julialang.org/t/julia-adoption-method-confusion/128672/8 "2025-05-07T17:27:57Z")

</div>

How can I be save from these misuses, and detect them? Is that somewhere documented?

---

<div class="post-metadata">

**Author:** ![goerz](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/goerz/32/3269_2.png) [@goerz](https://discourse.julialang.org/u/goerz)\
**Post date:** [May 8, 2025, 12:31am UTC](https://discourse.julialang.org/t/julia-adoption-method-confusion/128672/9 "2025-05-08T00:31:35Z")

</div>

You can’t be safe, at the end of the day. This is why multiple dispatch is a two-edged sword. The most safety you can get is by testing the assumptions your code is making.

Personally, I’ve had a good experience with defining [formal interfaces via `check_…` functions](https://juliaquantumcontrol.github.io/QuantumControl.jl/stable/api/reference/#QuantumControlInterfacesLocalAPI) that get called automatically on the arguments of high-level routines unless `check=false` is passed.

Presumably, some implementation of “traits” could do something similar with more efficiency but less flexibility than tests.

---

<div class="post-metadata">

**Author:** ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)\
**Post date:** [May 8, 2025, 3:51am UTC](https://discourse.julialang.org/t/julia-adoption-method-confusion/128672/10 "2025-05-08T03:51:20Z")

</div>

Relevant open doc PR of mine:

- [manual: methods: new section on avoiding ambiguity: "playing nicely" by nsajko · Pull Request #58005 · JuliaLang/julia · GitHub](https://github.com/JuliaLang/julia/pull/58005)

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [May 8, 2025, 8:27am UTC](https://discourse.julialang.org/t/julia-adoption-method-confusion/128672/11 "2025-05-08T08:27:59Z")

</div>

> [@ShalokShalom](#):
>
> How can I be save from these misuses, and detect them? Is that somewhere documented?

There is no language level feature protecting you from misuse in general. How could that be? Programmers an ingenious when it comes to misuse. Many of us have seen C++ code in the wild that looks like

```cpp
public calculate_tree_depth() {
  return 1; // FIXME code up calculation
}

```

that ticks all the boxes and makes the compiler happy. In ideal cases, it is caught in code review, but frequently not.

That said, Julia has _tooling_ for catching mistakes, including unit tests, analyzers like JET.jl (it catches a lot of errors about missing interfaces), etc.

But the key thing about this tooling is that it is _opt-in_: you invoke it at the point you think it makes sense for your code. This is because the language is designed for interactive development and quick prototyping: some of your code will run even if there are pieces missing.

Consider sending

```julia
struct MyFancyVector{T,S} <: AbstractVector{T}
    contents::T
end

```

to the REPL. At this point it does not support the formal interface of `AbstractVector` as it has no methods. I can fill them in later as I code, or redesign the whole thing (in 1.12 this is seamless as you can redefine `struct`) and then adapt the existing methods.

Users of pre-compiled languages come to Julia with the wrong kind of expectations: they are used to an environment where you are supposed to make the compiler happy first before you get to do anything. They have to learn a different coding style and a different set of QA tools. Frankly, not everyone will like doing this, and that is fine, no one is forced to use Julia.

---

<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:** [May 8, 2025, 8:54am UTC](https://discourse.julialang.org/t/julia-adoption-method-confusion/128672/12 "2025-05-08T08:54:29Z")

</div>

Just to preface, this could warrant splitting the topic because it’s getting farther from the original topic of not intuitively knowing what method a call dispatches to. Or it may not because this is precisely about failing to realize the ambiguous methods a possible call will fail to dispatch to.

That PR doesn’t actually capture the cause of the ambiguity. It strictly blames uneven argument type annotations, that is `foo(::A, ::B)` where `(A <: B) && !(B <: A)`, pointing to real code in MultivariatePolynomials.jl:

```julia
Base.:(==)(p::RationalPoly, q::RationalPoly) = p.num * q.den == q.num * p.den
...
Base.:(==)(α, q::RationalPoly) = α * q.den == q.num
Base.:(==)(q::RationalPoly, α) = α == q

```

The consequence is that while MultivariatePolynomials.jl itself would dispatch fine, another package that tries to extend `==` in the same way will create sources of ambiguity for code that mixes the packages, very possibly including itself. To adapt the general example to this case:

```julia
module MyModule
struct MyType end
Base.:(==)(x::MyType, y::MyType) = ...
Base.:(==)(x, y::MyType) = ...
Base.:(==)(x::MyType, y) = ...

```

A user tries:

```julia
using MultivariatePolynomials, MyModule
MyType(...) == RationalPoly(...) # MethodError!
#=
Candidates:
Base.:(==)(x::MyType, y)
Base.:(==)(α, q::RationalPoly) 
=#

```

The thing is, the same consequence can happen with even argument type annotations. Let the first package have:

```julia
module A
foo(x::Real, y::Real) = ...

```

And another package does:

```julia
module B
using A: foo
struct MyNum <: Real end
A.foo(a::Number, b::MyNum) = ...
A.foo(a::MyNum, b::Number) = ...
A.foo(a::MyNum, b::MyNum) = ...

```

so a user tries:

```julia
foo(3.14, MyNum()) # MethodError!
#=
Candidates:
foo(x::Real, y::Real)
foo(a::Number, b::MyNum)
=#

```

To visualize it, drawing a line through columns of parts of the type hierarchy for each ambiguous method signature will show intersections, whether it’s within the same branch (`MyNum <: Real <: Number`) or across different type branches (`RationalPoly` vs `MyType`) with a shared parent node (`Any`). Swapping uneven argument type annotations would produce more of an X, but the intersection can also have a fully horizontal line. The only method signatures whose lines CANNOT intersect any other’s are:

1. all leaf type annotations: this includes `::Type{T}` for type input `T` and concrete types for everything else
2. all `::Any` annotations for arguments, and the callable type annotation that only strictly subtypes `Function` or `Any`. That’s because `Function`, `Any`, and direct type parameters are disallowed in the callable’s type annotation.

 ![image](https://global.discourse-cdn.com/julialang/original/3X/e/0/e03860165e6f6a94e695231a5ca97bb871812b6a.png)  
While it does help to discourage shared supertypes (`Any` is the easy one) in order to separate type hierarchies per position, it’s important for functionality sometimes. The annotations pattern in MultivariatePolynomials.jl or module `B` are also important for functionality and widely used (and [documented](https://docs.julialang.org/en/v1/manual/methods/#man-ambiguities)) to resolve method ambiguities. Currently, I think there are two takeaways:

1. An acknowledgement that extended functions e.g. `Base.==` do not need to support arguments mixing types among unrelated dependents e.g. `MultivariatePolynomials` vs `MyModule`. Putting aside the lack of need and likely impossibility, one of the packages had to be aware of the other to implement `==`, and it’s not reasonable to expect everyone to know what everyone else is doing with `==`. A dependency implementing its interfaces well enough to work well in a dependent’s new contexts is already difficult enough, we shouldn’t expect the infeasible from composability.

2. If one package is in fact aware of the other (`B` is clearly aware of `A` through the extended function `foo`), there is a responsibility to make the types work together without ambiguity in the extended method table or resort to a new function with a fresh method table. If you can get away with it, only use type annotations in your own type hierarchies (iffy on exactly what is necessary, I think only one position is needed, a stronger version of what’s suggested to prevent type piracy from breaking preexisting code). In short, know the method table or leave it alone!

---

<div class="post-metadata">

**Author:** ![CameronBieganek](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cameronbieganek/32/6915_2.png) [@CameronBieganek](https://discourse.julialang.org/u/CameronBieganek)\
**Post date:** [May 8, 2025, 1:22pm UTC](https://discourse.julialang.org/t/julia-adoption-method-confusion/128672/13 "2025-05-08T13:22:24Z")

</div>

Just to emphasize what @Tamas_Papp mentioned, generic programming is _the_ solution to the problem quoted by OP (“That’s messy, you quickly drown in methods, and loose sight over what signature calls what body”).

Generally speaking, a Julia function should have a single generic meaning or semantics that is independent of which particular types the function is called with, so we don’t actually need to know which method of the function gets called.

Consider the following example:

```julia
"""
    mylast(x)

Iterate through every element of the iterator `x` and
return the last value iterated. The default definition
of `mylast` has O(n) time complexity, but some types
might define more efficient implementations.

Throw an `ArgumentError` if `x` is empty.
"""
function mylast(x)
    tup = iterate(x)
    isnothing(tup) && throw(ArgumentError("Iterator is empty."))
           
    local val
    while !isnothing(tup)
        val, state = tup
        tup = iterate(x, state)
    end
        
    val
end

function mylast(x::UnitRange{<:Integer})
    x.stop >= x.start || throw(ArgumentError("Iterator is empty"))
    x.stop
end

```

The `mylast` function has two methods, but it has a single generic definition. The calls `mylast([1, 2, 3])` and `mylast(1:3)` have the same behavior, so we don’t need to worry about which of the two `mylast` method definitions actually gets called under the hood.

(I guess that example doesn’t use multiple dispatch, but the principle is exactly the same for functions with multiple arguments.)
