Part 4 will talk about Clojure.
I just couldn't put it all in one piece - even broken to chunks it's of formidable length
Another distinction is that while Common Lisp and Julia both have parametric types, in CL you cannot use multiple dispatch on parametric types. Since parametric types are a very useful for writing generic, reusable libraries, not being able to dispatch on them is a significant limitation.
There's also the issue of performance. In Julia even something as basic as mixed-type addition – e.g. `1 + 2.5` – is defined in terms of layers of generic functions, but the compiler is clever enough to boil it down to a couple of machine instructions. This allows multiple dispatch to be used everywhere, which is really transformative. I'm not aware of any CL implementation which can eliminate the abstraction overhead of multimethods so completely.
It seems a bit remiss to have a series on multiple dispatch and never mention the two languages – Dylan and Julia – that take the paradigm the furthest and most seriously.
And Perl 6.
> in CL you cannot use multiple dispatch on parametric types.
You can in Perl 6.
> In Julia even something as basic as mixed-type addition – e.g. `1 + 2.5` – is defined in terms of layers of generic functions, but the compiler is clever enough to boil it down to a couple of machine instructions. This allows multiple dispatch to be used everywhere, which is really transformative.
MD has been used everywhere in Perl 6.
Perl 6 has maturity issues, including performance. But I see these as temporary issues.
> It seems a bit remiss to have a series on multiple dispatch and never mention the two languages – Dylan and Julia – that take the paradigm the furthest and most seriously.
Are you omitting Perl 6 because you don't think it's ready for prime time or you don't know enough about it's MD features?
------
I note that the OP series is about "runtime subtype-based polymorphism". I'm surprised at the exclusion of compile-time MD in a series which purports to be a general guide.
Both. Are basic operations like `+` generic in Perl 6?
Yes.
In Perl 6 this function definition implies a generic function:
multi concatenate (\foo, \bar) { foo ~ bar }
This function definition does not: sub concatenate (\foo, \bar) { foo ~ bar }
If one views the latter as the "default" (because it's two characters shorter and might be learned first) then Perl 6 has the same weakness, or at least the same default, as CL, compared to Julia and Dylan.In the default standard this might be true to some extend. We are talking about ANSI Common Lisp here. Though ANSI CL has several places where CLOS comes into play. In real implementations it's not true at all. ANSI Common Lisp is a minimum standard (even though it is large) and implementations are free to extend the use of CLOS - which they do.
Even though Dylan had the goal of efficient CLOS-like OOP, the Dylan spec was not very detailed about that - there were individual implementations. But how fast were they really? Difficult to say: Apple Dylan was only published as a technology demo. Harlequin Dylan was short-lived.
Julia OTOH is a language and its implementation. It brings some good ideas how to make OOP performing better. If you compare Julia with CL implementations like CCL, LispWorks, SBCL or Allegro CL - you will find that CLOS is widely used there. Much more widely than the ANSI CL standard defines. For example the base LispWorks on my Mac implements 1600 classes. It has 32000 functions of which 5182 are generic functions - and 10000+ methods.
So claiming that Common Lisp implementations are mostly not extensible via CLOS is not giving the whole picture.
I/O Streams are not CLOS based. In most/many implementations they are. There are one or two quasi-standards for that: Gray streams is one.
Conditions might be CLOS based. In most implementations they are.
CLOS is somewhat CLOS based. In most implementations it is fully CLOS-based with a meta-object Protocol.
There is a minimum ANSI CL, which makes it possible that individual implementations are mostly CLOS based. Then there is a larger Common Lisp, which assumes CLOS-based error handling, streams, CLOS itself, CLOS-based networking, CLOS based software management (ASDF).
Dylan and CLOS had different goals. Dylan was developed for application development where delivery and performance was focus. CLOS focus was on flexibility, expressiveness, extensibility, meta-object protocols, etc etc.. Performance was a topic in two ways: First: it should be possible to write large extensible libraries and applications in CLOS (like the typical GUI or networking code). Second: for applications that use CLOS for more performance critical things (like a CAD system with zillions of CLOS objects describing a 3D model) it was assumed that individual implementations provide additional features (like sealing, efficient slot access, etc.).
Writing high-performance numeric code (or similar) in a CLOS based way was not really thought of. It's definitely useful to extend higher-performance implementations with an optimizing compiler (like SBCL, ACL, LispWorks, ...) so that more performance critical code can be written in CLOS. But keep in mind that not all CL implementations have this goal of highest OOP performance. Many have other goals and already lack the attempt to provide an SSC.
SSC = sufficiently smart compiler