Claim (false): Julia isn't multiple dispatch but overloading

You could write universal function in statically typed C++ too:

#include <iostream>

// you define fnc once i.e. you don't need to specialize it for every type
template <typename A, typename B>
void fnc(A a, B b){ std::cout << (a + b) << std::endl; }

// you could use any object that support + operator and << operator (with std::cout)
struct C{
  std::string s;
  C operator+(C b){ return C{s+b.s}; }
std::ostream& operator <<(std::ostream& io, const C& c){ io << c.s; return io; }

int main(){
        fnc(1, 1);
        fnc(1.1, 1.1);
        fnc(C{"hello "}, C{"world"});


hello world

I don’t say that C++ is as flexible and expressible as Julia.
I am just afraid that accepted answer contains false claim.

1 Like

I did not say that it was not possible to achieve this in other ways in C++. I said it was prohibitively difficult to do it with overloading alone. Of course one could certainly use generic programming (in C++, templates) to accomplish something similar, although this comes with a whole range of issues that are out of scope of this discussion.


I am just curious. How would you call that something similar?

Is it overloading or not? Is it multiple dispatch or not?

And why?

1 Like

clearly you could achieve static overloading with static overloading alone, it’s the late binding that is hard to achieve:

As you probably already know, template gets obscure and hard to extend very quickly once the usage stops being “literally copy-pasting the same function body for every possible type”.


No, templates and generic types are not multiple dispatch. However, they can be seen as a static type solution to one of the problems I described, namely the redundancy of defining functions for multiple types.

As @jling pointed out, C++ templates are pretty much literally just syntactic sugar for copy-pasta. This can imitate a limited form of multiple dispatch by simply auto-generating static overloads for functions, assuming that all of the relevant types are known at compile time. However, it’s not “true” multiple dispatch in the sense that (1) it happens at compile time (early binding) and (2) it’s not polymorphic. In order to use polymorphism in C++, you need classes/objects, which only support single dispatch.

If you want to continue discussing the differences between C++ and Julia, I would suggest starting a new topic on the issue.


Saying just syntactic sugar is … questionable. You could similarly say that higher level programmer language is just syntactic sugar for assembler or machine code.

You say that “true” dispatch need to be polymorphic. But … :

It seems that you and Stefan are using term dispatch differently. Or is Stefan talking about “untrue” dispatch?

No I didn’t want to discuss about differences between C++ and Julia. I just felt some thing are not properly “solved” and some terms were used in “non stable” meaning.

But I am not interested to dig deeper if there is no mutual interest to improve understanding.

It was really interesting topic! :slight_smile:

For me - this is the best answer to @dataSurfer’s original question in this topic:

This POV surprised me. But if you think about it it feels like it Julia really is. (and it could be good for some kinds of problem and wrong for other kinds)

I also liked @dataSurfer @code_warntype example and @tim.holy’s @code_llvm one.

But this surprised me too: Julia use C infrastructure to achieve multiple dispatch. :open_mouth:

1 Like

no one would object the technically true statement that you can write everything with machine code. But it’s always about:

Saying “C/C++ can achieve everything Julia can because zero-cost abstraction, QED” is not useful, because possibility != people write useful, maintainable, extendable software with the hypothetical pattern. The reality is simply that, common pattern in C/C++ doesn’t give you what multi patch gives you and more importantly, they behave differently.

As you have observed:

Languages that uses LLVM as their compiler doesn’t mean these languages are basically C++. Rust is not C++, Python is not C. What being used to implement compiler is almost not relevant at all.


And I fully agree with you.
There are many things you could simply express in Julia and you have difficulties to achieve in C++. I will not argue here.
It is why I argue that syntactic sugar is good and important thing.

Maybe I am wrong but I think that code in gf.c is compiled, linked and used in runtime and not just during compile phase.

1 Like

No, this is not the same thing. Templates implicitly generate code in the same language (C++), while here you are referring to the process of compilation or machine translation. Syntactic sugar != compilation.

No, we aren’t. @StefanKarpinski and I are referring to the same thing. The virtual keyword induces polymorphism, i.e. it states “this method can be overridden and invoked dynamically on subtypes”. In single dispatch, this invocation is determined by the (implicit) first argument.

This was the point of my entire paragraph on why “compile-time” in Julia means something entirely different than in C/C++, Java, etc. Julia is inherently dynamic, all the way through. The compiler + LLVM is just a clever way to recover the performance typically lost in dynamic code interpretation.


9 posts were split to a new topic: OLS: difference between \ and inv (in normal equation)

I think future visitors of the page may want to read this:

Reason is in my honest opinion the true sentiment of the original claim and the push force of frequent recurrent revival is that: yes, Julia semantics are inherently dynamic but everytime you need performance (which is a very strong selling point for the language), you need to make sure the actual behavior can be devirtualized, effectively reconciling with static overloading.

Ideally I would post this even under the celebrated YouTube video (The unreasonable…), but YouTube has no good facilities for screenshot embedding or nice external link rendering :slight_smile:

No, devirtualization is not the same as static overloading. Static overloading is determined by the types declared at the method-definition site, but devirtualization occurs based on the types inferred at the call site.


Thanks for the clarification (this* is rather technical and subtle to me, but I see your point) :slight_smile:

*what is actually obscure to me at this point is how devirtualization is different from call-site inference and why the latter is faster (but this amount to me knowning what devirtualization really is…)