Stream: beginners

Topic: Performance costs - multiple dispatch for operators


view this post on Zulip Jonathan (Jun 15 2026 at 14:00):

I'm aware it's been discussed in the past and been ruled out for both compile time performance and its semantics, but given how much of a help it seems to be in numerical applications, could someone help me understand the extent of the performance implications if some form of multiple dispatch were introduced? I've searched back as far as Zulip history will let me, sorry if this was a common discussion before!

E.g given restrictions like

I would have thought (citation needed) that the penalty will be limited, as long as it doesn't become widespread practice across the community?

Not to belabour the point, but this would also enable default arguments, as well as fallbacks or default implementations (eg if the argument implements only is_lt and is_eq but not is_gt). I can understand why that may not be desirable though.

edit: I do understand also the undesirability of APIs that use inscrutable operator overloading, but I was wondering if there was a narrower version of multiple dispatch that could at least tackle the performance issues.

view this post on Zulip Notification Bot (Jun 15 2026 at 14:02):

A message was moved here from #beginners > Type based pattern matching for operator overloading by Jonathan.

view this post on Zulip Norbert Hajagos (Jun 16 2026 at 08:05):

I'm not really the guy for this, but I know multiple dispatch requires run time type information (rtti) to be stored alongside the values. There might be a clever way to do that which doesn't involve boxing, like storing the metadata before the value, but it would still have pretty big impact on scalar types. It's why int-s are not objects in Java. They need to be stack allocated, unboxed to achieve reasonable performance. We're aiming to be faster than go and one way we do that is with less boxing. No interface objects, but rather static dispatch. Even closures are stack allocated in Roc.

I would also imagine platforms to have issues with rtti. Either each re-implement dispatching, or exposed functions would be wrapped in another function that would handle the dispatching. But then what are the types that should be accounted for in code-generations? All of them? That's not possibel. You would need to restrict exposed functions. Not a big deal, ppl wouldn't expect it to work across language boundaries, but still.

One of the nice properties of static dispatch as it stands currently in roc is that operators desugar to simple methods i.e. functions. If you implement certain functions, you've implemented the operator. This would no longer be the case if we make operators special. Idk if that's a worthwhile tradeoff, but something to keep in mind.

Someone who knows more about multiple dispatch should correct me though, as this is just my brain-dump.

view this post on Zulip Jonathan (Jun 16 2026 at 09:04):

Oh yeh of course, if it did require rtti it would be a non-starter. But I was wondering only about multiple dispatch where the variant can be determined statically. I.e.

vec.plus(2)

would be determined at compile time as the variant that accepts a where [a.to_i32 : a -> I32] without need for dynamic dispatch. Similarly,

get_screen_pos : Vec2(F32), F32, Vec2(F32) -> Vec2(F32)
get_screen_pos = |world_pos, zoom, offset| world_pos * zoom + offset

In situations where it is not possible to determine which variant from only local context, it would be a compile error, which would both limit compiler work and make it clear as a reader which variant is being used.

If you implement certain functions, you've implemented the operator.

I believe it still would be: you still have your module define is_eq, but it can also define another is_eq for a different set of types.

This would no longer be the case if we make operators special.

The way I see it, operator methods are already special because of their privileged use in syntax. Permitting dispatch to plus(I64), plus(Vec) etc. takes advantage of that terseness.

view this post on Zulip Jonathan (Jun 16 2026 at 09:25):

Anyway I don't want to hammer on about it if it has been ruled out :sweat_smile: Just seemed like there was still a direction available there.

view this post on Zulip Brendan Hansknecht (Jun 20 2026 at 19:58):

I think it has been discussed in the past and leads to some behaviours that can be confusing.

For example, it often breaks symmetry or requires being able to extend already existing types to implement well.

I definitely see the general argument in general for static dispatch being extended with essentially overloading, which I think is the core ask here

view this post on Zulip Brendan Hansknecht (Jun 20 2026 at 20:00):

I have heard of complaints/bugs from the first match semantics for example.

view this post on Zulip Brendan Hansknecht (Jun 20 2026 at 20:03):

Also. I though we had a plan to allow from_numeric or something like that to enable exactly what you are showing. Not sure what happened to those ideas. Also, not sure if they had issues or not

view this post on Zulip Richard Feldman (Jun 20 2026 at 20:03):

I don't know where in the video it was, but the most recent time I had Chris Lattner on Software Unscripted he basically said Hindley-Milner + overloading destroyed Swift's compile times and he wouldn't do it again

view this post on Zulip Richard Feldman (Jun 20 2026 at 20:05):

frankly I think higher kinded polymorphism has a higher chance of ever being in Roc than multiple dispatch :sweat_smile:

view this post on Zulip Brendan Hansknecht (Jun 20 2026 at 20:11):

Ok...I thought there was a way around this without multiple dispatch via where causes, but as I type it out, I think I'm wrong.

Even if it does work, it would definitely require extending builtin types or some weird encode + decode step. So I don't think it would make sense in practice

view this post on Zulip Jonathan (Jun 20 2026 at 21:36):

Richard Feldman said:

I don't know where in the video it was, but the most recent time I had Chris Lattner on Software Unscripted he basically said Hindley-Milner + overloading destroyed Swift's compile times and he wouldn't do it again

Ah, yeh that rings a bell. And fair enough, better to not follow in Swift's footsteps :joy:

view this post on Zulip albx (Jun 27 2026 at 10:52):

Maybe it's a very stupid idea, but could a+b desugar to (a,b).plus()? Then it would do single dispatch on the tuple... Kind of like multiple dispatch, but cheating...

view this post on Zulip Luke Boswell (Jun 27 2026 at 11:16):

The + binop does de-sugar like that. The desugar section in langref isn't written yet, but there is an example at https://github.com/roc-lang/roc/blob/main/docs/langref/static-dispatch.md#plus

view this post on Zulip Luke Boswell (Jun 27 2026 at 11:17):

Or maybe I misunderstood the idea.

view this post on Zulip Anton (Jun 27 2026 at 13:46):

Looks like it is a.plus(b), this is for 4 + 2:

        (e-dispatch-call (method "plus") (constraint-fn-var 139)
            (receiver
                (e-num (value "4")))
            (args
                (e-num (value "2"))))

view this post on Zulip Lukas Juhrich (Jun 27 2026 at 15:12):

Richard Feldman said:

[…] Hindley-Milner + overloading destroyed Swift's compile times and he wouldn't do it again

Maybe all of you know this, but:

This sounds about right, appearently \lambda-calculus plus overloading is already NP-complete (read: „requires a SAT-solver“):

https://web.cs.ucla.edu/~palsberg/paper/dedicated-to-kozen12.pdf

Although one should point out that „overloading“ here means ad-hoc polymorphism and not parametric (generic) or ranked (inferred dimensionality) polymorphism.

I'm not sure where rocs current capabilities place its type inference/checking at the moment though with regards to complexity (and would like to know).

view this post on Zulip Jared Ramirez (Jun 27 2026 at 15:31):

albx said:

Maybe it's a very stupid idea, but could a+b desugar to (a,b).plus()? Then it would do single dispatch on the tuple... Kind of like multiple dispatch, but cheating...

very close, plus desugars to a.plus(b)

view this post on Zulip Jared Ramirez (Jun 27 2026 at 15:38):

roc does not have (or intend to have, at least atm) rank-n types, nor do we have higher kinded types


Last updated: Jul 23 2026 at 13:15 UTC