A polyglot's guide to multiple dispatch – part 4
eli.thegreenplace.net
eli.thegreenplace.net
https://news.ycombinator.com/item?id=11526923 [Intro, C++]
If you didn't know, Clojure's dispatch mechanism is based on the Common Lisp library, filtered functions[0], which is described in this paper[1].
Doing it at compile-time is a feature, because the compiler has more information available for selecting the right specialization. (Technically, this can be done at runtime as well, but only at the price of making dispatch insanely expensive.) For example, in CLOS, how would I do the equivalent of specializing a template for when its argument is a `vector<list<unique_ptr<int>>>`?
But since your whole language is available at compilation time, there is nothing preventing you to define a DSL for a subset of types that are fixed. You could even use compiler macros to deal with trivial cases. Then, you add enough "(declaim (type ...))" directives in your language and augment the optimization settings to allow your implementation to trust declarations without adding dynamic checks.
If you need to have access to more type informations, use the implementation-specific tools to walk your code and define custom transformations (http://www.pvk.ca/Blog/2014/08/16/how-to-define-new-intrinsi...) when generating assembly.
The only thing that prevents doing the same work than in C++ is that by default, you can redefine your class hierarchy at runtime: your server is running, you change your class definition and instances of that class have, for example, a new slot, properly initialized. Or, you change the class of your object (square -> rectangle).
In other words, the type system is unsound. Which is exactly as useful as having no type system at all.
But, anyway, what I asked for is (0) types parameterized by other types [e.g. "dictionary mapping ints to strings"] and (1) dispatch [e.g ither static or dynamic] on parameterized types. How would you solve this challenge? https://news.ycombinator.com/item?id=11592693
For example, if you have a method specializing on the type (satisfies a) and a method specializing on the type (satisfies b), and you call a generic function with an object that satisfies both a and b, which method should be called first?
Or methods specializing on the types (integer 0 8) and (integer 1 9). If the argument is 5, which type is more specific?
Classes are a subset of types with a certain hierarchy, so there is always a "correct" order for methods to be called in.
The point is that C++ has a much richer type structure on which you can dispatch.
To be honest, I think folks imagine the wrong things when looking at CLOS. They think it's Lisp foray into statically-typed OOP like C++. But it isn't. Lisp stays dynamically typed. The task you're proposing is nonsensical "in CLOS". It's just not Lisp-y :-)
I think this discussion has gotten fairly far from the main point by now. There's no real use trying to twist one language to achieve every feature of another.
This isn't a matter of this language or that language. Are you going to deliberately use a less efficient algorithm or a more convoluted design, just to please your language of choice? No idea about you, but, when a programming language doesn't let me express what I want, I don't force myself to want something else - I switch languages.
> To be honest, I think folks imagine the wrong things when looking at CLOS. They think it's Lisp foray into statically-typed OOP like C++. But it isn't. Lisp stays dynamically typed.
I never said CLOS is Lisp's take on static typing. I'm just offering an example of a use case for which CLOS isn't as powerful as other existing alternatives.
> The task you're proposing is nonsensical "in CLOS". It's just not Lisp-y :-)
This doesn't make sense. What I'm asking for is fundamental if you want generic programming to work: generic algorithms must be specializable for performance reasons, while still providing a uniform interface to users.
That would be fine with me, but...
> I don't think CLOS supports this use case, but the rest of Lisp certainly does.
How exactly? Manually branching on the type of an object is ugly and non-extensible, and thus the antithesis of good modular design. Or are you proposing creating a whole new object system, in parallel to CLOS, but which supports this one additional feature? My copy of AMOP suggests that the design of CLOS is intended to avoid this kind of scenario:
“The metaobject protocol approach (...) is based on the idea that one can and should "open languages up," allowing users to adjust the design and implementation to suit their particular needs.” (p. 1)
“Rather than supplying the user with a fixed, single point in the space of all language designs and implementations, we would instead support a region of possible designs within that overall space. (...) Users are free to move to whatever point in that region best matches their particular requirements.” (p. 5-6)
...but you can do it in Clojure, right? (honestly I don't know anything about Clojure). I guess some diehard old-school lispers don't consider Clojure lispy. Is there another way it isn't lispy? Certainly someone could come up with generic functions that dispatch on types, and then the problem would be trivial. The "filtered-functions" mentioned above, seems to have the machinery required.
If it's static like possible in some ways in C++, then Common Lisp won't support it, because it generally lacks a full featured compile-time static type system.
> could come up with generic functions that dispatch on types
But how would you know that a vector only contains ints between 100 and 200 or characters from A to Z? Either a) we can express that in the type system for compile time, we carry complex type annotations for runtime objects, or we do a check at runtime.
CLOS: Dynamic dispatch is possible, given extensions like predicate dispatch.
ftp://publications.ai.mit.edu/ai-publications/2001/AITR-2001-006.pdf
Can be implemented in CLOS or Common Lisp? Are you thinking of something using the metaobject protocol? Catnaroek's challenge is interesting, and would seem on the surface to not be possible with CLOS.