What was reason that popular programming language such as Java
, c++
, python
didn’t officially supported multiple dispatch
instead used single dispatch oop
. They still didnt corrected it . Why??
Even the Creators have said they were not 100% sold on it at first, and then gradually realized how well it worked. It may be a threshold effect, poorly implemented or half-way committed multiple dispatch isn’t that great, the concept was waiting for someone to decide to really go for it full tilt and succeeding.
At least for Python and C++, there are proposals for it.
Without aggressive devirtualization, as in Julia, multiple dispatch is probably harder to make as performant as single dispatch with vtables. But devirtualization in C++ is hard unless people use more final
classes, which hasn’t been common in C++ culture. (In contrast, all concrete types in Julia are final.)
These performance issues probably don’t concern Python much, unlike C++. But still no multiple dispatch in the language…
(there are not-really-used libraries for this though)
I just noticed in wikipedia, that multiple dispatch is supported in C# for 10 years already. This is likely the most popular language with built-in MD. Can anyone knowledgeable in C# clarify whether their approach does really differ from Julia’s?
One thing that strikes me when comparing Julia to other languages it that it was not created by one individual. Think about what names you associate with various programming languages - Python/GvR, C/Ritchie, Java/Gosling, Ruby/Matz, C++/Stroustrup, APL/Iverson, and so on.
I don’t know much about the early years of Julia, but it is generally credited to four people and there’s clearly a larger circle of folks who have made very significant contributions between it’s inception and version 1.0, and an open trail of evidence of how the design was worked out. How many threads from newcomers asking “Why does X work like it does?” are answered with a rundown of what was considered and weighed and the compromise reached?
I’m not saying it’s the only reason Julia is succeeding with multiple-dispatch where others didn’t. There’s a convergence of enabling technologies (LLVM, PCs with lots of RAM, git and the related open-source environment) that weren’t present for the creation of languages ten (or fifty) years ago. However, I suspect it was an essential ingredient in making it a the well-rounded product that it is.
One part of this story that I think is really interesting is that multiple dispatch was never made a major focus of a “slow” language like python. The world in which people realized multiple dispatch was great and then tried to make it fast makes way more sense than the one we live in.
GvR [Guido van Rossum] in 2005: “I used to believe that multimethods were so advanced I would never need them.” That’s about 15 years after he made Python (and about 5 years after I started using Python).
Source: Five-minute Multimethods in Python
EDIT: I mean no disrespect to GvR. He made an amazing tool that helped a lot of people do more than they would have without it.
It’s hard for me to formulate longer, but following the definition in Style Guide · The Julia Language
I know what type piracy is, I’m just confused as to why you brought it up. Stefan’s example didn’t have any type piracy.
The issue in the manual is that they have imported the the function, and then added a new method to it. Had they merely made a new function with the same name it wouldn’t have been type piracy.
I interpreted it as something like “the fact that type piracy is possible is scary/bad/a flaw of Julia’s paradigm” (which I have heard occasionally in various forms from other people). I can see how it’s a bit scary (because mutating global state is scary, and as you know method tables are global state that you mutate when you load a package), but I think the Julia ecosystem has done a great job being disciplined about it so it’s not much of a problem in practice.
And of course sometimes it’s super valuable/productive to be able to pirate a method in non-package code and keep working instead of forking, adding the method, making a PR, using the fork until it’s merged, etc (though I still open an issue or PR when I find the need to pirate). And by committing projects + manifests it’s pretty safe too.
Type-piracy is basically a feature.
It means a user of two libraries can make them work together, without changing them.
It takes a problem which is unsolveable without changing someone elses code, and makes it solvable.
I find it is helpful to break type-piracy into two kinds.
Felony Type-Piracy, and Misdemeanor Type Piracy.
Misdemeanor type-piracy is where you use type-piracy to change code that would have errored into code that gives a sensible result. (especially if it is a MethodError
).
This is rarely a problem, since no code is depending on (since we don’t have a pattern of using exceptions for non-exceptional circumstances in julia) it, since it errrors.
No existing behavour is changed.
It is a bit awkward if someone else also defines that same thing, and you load both and then make guilty eye-contact (and there is a warning message).
but its only a problem if someone else defines it differently.
Avoiding type-piracy in this case makes it impossible for this two definations thing to occur (assuming you also don’t violate the package manager by using undeclared dependencies and Revise.jl), since there is only at most one package that could define it (its is what ever is lowest in the dependency graph, and it only exists if every package is a dependency of the lowest).
Felony type-piracy is more of problem.
Felony type-piracy turns a non-error value into a different non-error value
Felony type-piracy is achieves monkey-patching
still a feature but more dangerious and pretty bad to use in serious code. If you have to then use =
or ~
version specifiers in your [compat]
, hold on to your manifest, and leave warnings in your README)
One of the effects of Felony type-piracy is spooky action at a distance.
Where If I remove using Foo
from my package since it wasn’t using any of its methods or types anywhere anymore, my results can can change.
And not merely becoming an error but becoming ba different answer.
Because Foo
mikght have redefined some other package’s function on types that I am still using to e.g. give a more accurate result; and with it gone i am falling back to a less accurate general method.
I think that type piracy is a red herring here, and just sidetracks an otherwise interesting discussion.
Well, I have additional view on this problem. During discussions in some threads about performance of vectors of non homogeneous objects, I’ve stumbled on few blog posts, where OOP approach is discussed, like this one for example: Data-Oriented Design (Or Why You Might Be Shooting Yourself in The Foot With OOP) – Games from Within
Maybe I am getting it wrong (and this blog is rather old), but it looks to me, that OOP approach leads you in the end to non optimal code, since when you have complicated class hierarchy and pack different objects in a container performance of your code is always low. Of course, you can avoid this problem by using Component pattern, but this means that you should basically ignore most of your OOP.
In Julia on the other hand it is quite natural to switch between different approaches, it is just struct-of-vectors vs vector-of-structs problem. It’s much easier to design library which utilize one approach or another or even use both with something like StructArrays.jl
Of course it is more of a mental problem, not language one. For me, data-oriented design described in that post seems more natural for Julia than for any common OOP based language.
I’m coming late to this discussion and I can’t claim to have read all the posts carefully so forgive me if I reiterate something already discussed. One of the central differences between the “methods defined within classes” style of OOP and the Julia approach of generic functions with multiple dispatch is, obviously, the ability to dispatch on the types of more than one argument. The “poster boy” application for why this is a good idea is numerical linear algebra. To achieve flexibility and efficiency in numerical linear algebra you need to be able to examine and exploit the types of all the arguments.
Over the past 30 years I have worked with many different linear algebra packages in Fortran, C, C++, and Python. I even had a hand in designing and writing the Matrix package for R. None of them come close to the coverage, flexibility, and efficiency of the LinearAlgebra standard library for Julia.
Of course, this isn’t an accident. Julia was designed for technical computing and the choice (and hideously efficient implementation) of multiple dispatch was intended for exactly these cases.
To be clear @Botant, type piracy is not something we encourage or condone in serious packages. It’s something that should be avoided through coordination between package authors and the creation of packages that facilitate this coordination.
However, type piracy is a fantastic tool for users or library writers who just need to get something done fast. Later on, once you’ve had time to talk to other package writers and discuss a solution, then the piracy should be removed.
I agree with @Tamas_Papp though that there’s a risk here that we’ve derailed a great discussion with the type piracy talk. Perhaps a moderator can split the piracy stuff into it’s own thread?
Would you say that multiple dispatch is particularly suited for more numerical code? Or put differently, is multiple dispatch less of an advantage when used for programming less “science-y” tasks, like business logic in a web server, system administration tools or (weird thing to come to mind ) implementation of a cryptocurrency?
I’m probably not the best person to answer that question because almost all the programming I do is “science-y” and, for me, a system based on generic functions and multiple dispatch, combined with the Julia’s type system, works well. I haven’t had situations where I thought, “Gee I wish I could use a class-based OOP system instead of defining types, generics and methods”.