# PSA: sometimes keywords affect dispatch

**URL:** <https://discourse.julialang.org/t/psa-sometimes-keywords-affect-dispatch/137646>\
**Category:** General Usage\
**Created:** [June 15, 2026, 10:28pm UTC](https://discourse.julialang.org/t/psa-sometimes-keywords-affect-dispatch/137646 "2026-06-15T22:28:28Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![MilesCranmer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/milescranmer/32/21070_2.png) [@MilesCranmer](https://discourse.julialang.org/u/MilesCranmer)\
**Post date:** [June 15, 2026, 10:28pm UTC](https://discourse.julialang.org/t/psa-sometimes-keywords-affect-dispatch/137646/1 "2026-06-15T22:28:28Z")

</div>

Did you know that keywords can actually affect method dispatch in Julia?

The Julia docs say they don’t:

> Keyword arguments behave quite differently from ordinary positional arguments. In particular, they do not participate in method dispatch. Methods are dispatched based only on positional arguments, with keyword arguments processed after the matching method is identified.

But, actually, there is an [11-year-old correctness bug](https://github.com/JuliaLang/julia/issues/9498) documenting a situation where keywords do affect dispatch. And this situation is much more common than you would think.

This bit me recently, and I think it’s quietly biting other people too. After pulling out a lot of my hair, I learned a lot about Julia’s dispatch system. Basically, there are effectively two method tables - one for positional-only methods, and one for anything with keywords. However, there is an issue here: if you call a function using _any_ keywords, it will immediately switch to the keyword table, and only search there. That means a more specific positional-only method will not be matched, and this re-routing happens silently!

For example:

```julia
julia> process(x; verbose=false) = "generic"
process (generic function with 1 method)

julia> process(x::Vector) = "specialized"
process (generic function with 2 methods)

julia> process([1, 2, 3])
"specialized"

julia> process([1, 2, 3]; verbose=true)
"generic"

```

Adding `verbose=true` routes the call to the _less_-specific method and silently skips `process(::Vector)`, searching the keyword table only. In other words, we see keywords affecting dispatch.

The same bug has a second symptom. Redefining a method without keywords doesn’t replace the keyword handling of an earlier definition with the same positional signature, even though Julia itself thinks there is only a single method:

```julia
julia> g(x; y=1) = "with keywords";

julia> g(x) = "without keywords";

julia> g
g (generic function with 1 method)

julia> g(1)
"without keywords"

julia> g(1; y=2)
"with keywords"

```

“1 method,” but two behaviors, depending on whether you pass a keyword. Spooky.

Under the hood, a keyword call `f(args...; kw...)` is lowered to `Core.kwcall(kw_nt, f, args...)`, a separate generic function that holds the keyword table. A positional-only method never gets an entry there, which is why it becomes invisible the moment you pass a keyword.

The case that really seemed widespread is `sizehint!`. In Julia 1.11, Base added a `shrink` keyword to `sizehint!`, which had previously been positional-only. It seems this slipped past a lot of people. Julia internals now call `sizehint!(d, n; shrink=true)` in several places (dict copy, `merge!`, `union!`), and that `shrink` keyword sends the call through the keyword path. So every custom `sizehint!(::MyType, n)` that never declared a `shrink` keyword is invisible to it, and the call quietly falls back to the generic Base version, which usually allocates _nothing_ and ignores your method entirely! So if you wrote a custom `sizehint!` for your type and didn’t know about the new `shrink` keyword, there’s a good chance Julia internals have been skipping it entirely. I suspect a lot of packages are hit by this one without realizing. (PkgEval turned up quite a few.)

* * *

So, what is the status? When I saw this, I couldn’t believe it was still a bug after 11 years, so I spent a lot of time trying to fix it in [#60499](https://github.com/JuliaLang/julia/pull/60499). The idea is simple: whenever you define a positional-only method, also emit a matching stub into the keyword table with the _same_ positional signature and zero keywords. So `kwcall` can no longer silently reroute to a less specific method; it throws an error instead.

The catch is it adds one extra method per positional definition, which slightly grows the sysimage, and the Julia team wanted a lower-overhead approach. A cleaner version rewriting dispatch would be a much larger project, and nobody has yet signed up for this. So for now this is just “what Julia does.” But at the very least, I think more people should know about it, hence this post.

The fix attempt also surfaced that this trips up Base itself. In several places a keyword got added to a previously keyword-less method, which then silently reroutes calls on more specific types to a generic fallback (the `sizehint!`/`shrink` case above is one). So it’s not just user packages; this is easy even for maintainers to forget when extending functions in Base. (I also opened a docs PR for good measure, [#61886](https://github.com/JuliaLang/julia/pull/61886).)

---

<div class="post-metadata">

**Author:** ![foobar\_lv2](https://avatars.discourse-cdn.com/v4/letter/f/ee59a6/32.png) [@foobar\_lv2](https://discourse.julialang.org/u/foobar_lv2)\
**Post date:** [June 15, 2026, 11:03pm UTC](https://discourse.julialang.org/t/psa-sometimes-keywords-affect-dispatch/137646/2 "2026-06-15T23:03:25Z")

</div>

We should be able to detect this, right? I.e. write a script that goes over the entire current method table and finds all these issues, i.e. all instances where `f(x)` and `f(x;(;)...)` get dispatched to different methods.

That would be very useful:

1. People would be able to check whether they are affected by this questionable behavior
2. This would probably detect a lot of real bugs, even more than test failures with your PR!
3. This would be a very good linter rule.
4. This also supplies political ammunition for your crusade to fix #9498

Don’t underestimate (4).

---

<div class="post-metadata">

**Author:** ![MilesCranmer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/milescranmer/32/21070_2.png) [@MilesCranmer](https://discourse.julialang.org/u/MilesCranmer)\
**Post date:** [June 15, 2026, 11:16pm UTC](https://discourse.julialang.org/t/psa-sometimes-keywords-affect-dispatch/137646/3 "2026-06-15T23:16:59Z")

</div>

Yeah it would be nice to have an Aqua.jl check for this. I’m not sure how they would detect it though.

Maybe the simplest might be to repurpose that PR’s strategy of injecting 0-keyword methods into the `Core.kwcall` table for each positional-only signature. Then it will simply throw whenever you hit this bug.

I remember the first PkgEval of that PR was pretty gory, mostly due to the `shrink` keyword added in Julia 1.11 which resulted in tons of Julia packages erroring out.

Even now, this is **still** a pervasive issue in the Julia ecosystem - there are many specific `sizehint!` methods getting skipped in favor of the generic no-op one that doesn’t pre-allocate at all because some internal Julia Base code uses an explicit `shrink` keyword.
