Julia is the first language to really show that multiple dispatch can be efficient in performance-critical code, but I'm not really sure why: JIT concepts were certainly familiar to implementors of Common Lisp and Dylan.
Julia is such a wonderful language. There are many design decisions that I like, but most importantly to me, its ingenious idea of combining multiple dispatch with JIT compilation still leaves me in awe. It is such an elegant solution to achieving efficient multiple dispatch.
Thanks to everyone who is working on this language!
I don't understand why people keep talking about multiple dispatch like Julia invented it. You can do that in many other languages, even languages designed for numeric computation. What's cool about Julia is that it has brought 90s compiler technology to scientific computing: a field which still thinks MATLAB is a really good way to develop and communicate scientific ideas.
That is not a very charitable characterization of the position of multiple dispatch in Julia. It's not something that's optional: multiple dispatch is essential for the performance that Julia is looking to achieve. If you notice where acceleration DSLs tend to have trouble, you'll notice that it's always at the point where you get beyond built-in float primitives and onto object support. For example, Numba's object mode has the caveat that "code compiled in object mode will often run no faster than Python interpreted code, unless the Numba compiler can take advantage of loop-jitting", where loop jitting is simply the ability to prove that some loop is compatible with moving to the nopython mode.
The reason why Julia is fast is because automatic function specialization to concrete dispatches gives type-grounded functions which allows the complete optimization to occur on high-level looking code (see https://arxiv.org/abs/2109.01950 for type-theoretic proofs). It's basically a combination of (1) define a type system in a way that allows for type-grounded functions and compile-time shape inference (shape as in, byte structure of the structs), (2) define a multiple dispatch system with automatic function specialization on concrete types, (3) have a typed IR which proves and devirtualizes all dispatches before hitting the LLVM JIT. If you simply slap the LLVM JIT on random code, you will not get that performance. But now because multiple dispatch is fundamental to performance in the language, the rest of the "game" for the language is how to design an ergonomic language around this feature and how to teach people to use it effectively as a problem solving tool.
You actually see something similar going on in the world of Jax. With Jax, you need to be able to perform abstract interpretation to the Jax IR. In order for this to be possible with the interpreters Jax has, the functions that are being interpreted need to always have the same computational graph for the same inputs, i.e. they need to be pure functions. This is why Jax is built on functional programming paradigms. It would be similarly uncharitable to say the reason why Jax does not embrace OO is because the developers just love functional programming: the programming paradigm choice clearly falls out of what the tools needs to do.
It remains to be seen if Jax is the tool that makes more people finally embrace functional programming styles, or if enough people see pervasive performance necessary enough to change to the multiple dispatch paradigm of Julia. But what is clear is that tools that are moving away from OOP are not doing so arbitrarily, it's all about whether doing so is beneficial enough to justify the change.
Multiple dispatch turns out to be truly awesome for mathematical code. In my opinion, the focus and careful selection of features for technical computing, while being a general purpose programming language is what differentiates julia from other dynamic languages.
I think the difference is that in Julia multiple dispatch is the main paradigm to structure code (together with very aggressive devirtualization/specialization/compilation). That enables quite amazing things. Other languages have multiple dispatch as well, but it is not foundational to the ecosystem in them. They lack the "magic" but they also have lower propensity for the interface mismatch bugs described through these two threads.
I think people often underestimate (or just plain don't know about) the degree to which a multiple-dispatch-based programming language like Julia effectively implies its whole own dispatch-oriented programming paradigm, with both some amazing advantages (composability [1], and an IMO excellent balance of speed and interactivity when combined with JAOT compilation), but also some entirely new pitfalls to watch out for (particularly, type-instability [2,3]). Meanwhile, some habits and code patterns that may be seen as "best practices" in Python, Matlab can be detrimental and lead to excess allocations in Julia [4], so it may almost be easier to switch to Julia (and get good performance from day 1) if you are coming from a language like C where you are used to thinking about allocations, in-place methods, and loops being fast.
Things are definitely stabilizing a bit post-1.0, but it's still a young language, so it'll take a while for documentation to fully catch up; in the meanwhile, the best option in my experience has been to lurk the various chat forums (slack/zulip/etc. [5]) and pick up best-practices from the folks on the cutting edge by osmosis.
Julia has a very nice type system, the nicest of any dynamically typed language I am familiar with. This is something to do with multiple dispatch, but it's more to do with trying to have a type system that allows all the JIT to unbox all the things that have to be unboxed for high performance without sacrificing the freedom of dynamic typing.
IIUC, Common Lisp is the giant on whose shoulders Julia built in this respect.
But the performance relies on the aggressive specialization which depends on multiple dispatch. And the adaptation to numerical computing, the cleanness and beauty is all about multiple dispatch.
To stay with the bike analogy, multiple dispatch is definitely the wheels, not the motor.
> for one, I 100% agree with your views on variable naming elsewhere on this thread ;)
Waaah! (pulls hair.) And yet, so far apart on the function naming ;)
But seriously, though. Without the incredible polymorphism and genericity, there's really nothing at all left of Julia. Multiple dispatch isn't a feature bolted onto Julia. It is the core philosophy, and the central organizing principle.
I've been using Julia for my thesis research lately, and I find multiple dispatch to be a great match for writing mathematical algorithms, especially because of the ad-hoc specialization that it allows based on all parameter types. OO approaches always seemed weird for this sort of thing, where you'd have to decide which type "owns" the ability of how to work with the other types.
What I wish for:
- faster lambdas and which capture numbers as values not as addresses. I left Python over the latter, it caught me up all the time. At least Julia developers don't consider it a feature and plan to fix it.
- to be able to specify function domain and range types, for documentation and sanity checking
- maybe some improvements in comprehensions would be nice, so I could have an Array of Arrays of something e.g.. Currently the [x for...] comprehensions tend to collapse things together while the {x for ...} comprehensions tend to lose the types of what's inside.
Overall a great language already and a positive experience.
Indeed, Julia's abstract type system with multiple dispatch is its killer feature. It enables generic programming in a beautiful and concise fashion. Here [1] Stefan Karpinski gives some explanations of why multiple dispatch is so effective.
Julia is a fairly elegant nice language and multiple dispatch makes a lot of sense in all sorts of places.
I don't really get why Julia is pigeon-holed for numerics. I prefer its syntax to Python and would consider it for scripting applications where I use Julia now.
But then again, Julia is a relatively new language that aims for high performance in scientific computing while using type inference. Helps to have multiple dispatch that tells you how many definitions there are for a function.
The main issue I encountered as a Julia user is that multiple dispatch doesn't scale very well.
When you start building out a project, it's easy to keep track and debug if multiple dispatch starts failing (i.e. <any> type starts spreading everywhere and Julia slows to Python like speeds).
In medium-to-large projects, it becomes extremely cumbersome to manage this. It's doable, but adds a layer of complexity management to projects that simply doesn't exist in strictly typed or pure scripting languages.
Of course, you can just decide to explicitly type everything - but the issue here again is the lack of enforcement.
In a nutshell: Julia is great when you're a grad student working mostly by yourself on small scale projects! But not so great in prod.
And there's really no problem with that; that's who the language was designed for!
“Julia features optional typing, multiple dispatch, and good performance, achieved using type inference and just-in-time (JIT) compilation, implemented using LLVM.”
The main difference is that Julia uses more complicated subtyping and specificity to let you express some useful more complicated relationships (e.g. diagonal dispatch).
Also, there is a major difference that in Julia all methods use multiple dispatch (and there isn't a performance cost to doing so). Multiple dispatch in Lisp was severely limited by people not using it for performance reasons.
The promise of multiple dispatch is that it allows every library to be generic. One author writes a library that calculates $COMPLICATEDTHING; another author writes a library that implements a new type of number, such as ranges; provided the second library defines all the methods that the first uses, then, magically, they work together and you can calculate $COMPLICATEDTHING on ranges! That is the thing that Julia does that nothing else does.
reply