Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
A polyglot's guide to multiple dispatch – part 4 (thegreenplace.net)
65 points by ingve on April 28, 2016 | hide | past | favorite | 25 comments



I posted this on the blog but I'm copying it here because I have to wait for approval on the blog.

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].

[0] https://github.com/pcostanza/filtered-functions

[1] http://www.p-cos.net/documents/filtered-dispatch.pdf


> In fact, multiple dispatch is best categorized as a generalization of OOP, being more in the domain of generic programming. Kinda like the generic programming of C++, just at runtime instead of being limited to compile-time constructs and metaprogramming.

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>>>`?


Lisp has a rich type system and there is no conceptual problem in having both static analysis and dynamic checks. What happens, especially with classes, is that you cannot guarantee that your type hierarchy will not be altered at the time you invoke your generic method (highly dynamic classes are a feature too).

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).


> What happens, especially with classes, is that you cannot guarantee that your type hierarchy will not be altered at the time you invoke your generic method (highly dynamic classes are a feature too).

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


Could you clarify if you are trying to make a comparison between garbage collection and unique_ptr? Or are you saying that you wish CLOS allowed dispatch on types as well as classes (anyone know the historical reason for why this is the case?). Or are you making the point that it would be ugly to create a new class "vector-of-list-of-fixnums"? Or something else?


CLOS only allows dispatch on classes and not types, because there is no hierarchy in types.

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.


It has nothing to do with garbage collection. I could've used GHC Haskell with sufficient extensions (MultiParamTypeClasses, FlexibleInstances, perhaps UndecidableInstances too) enabled as an example, instead of C++.

The point is that C++ has a much richer type structure on which you can dispatch.


Through things like this library, which, combined with a Sufficiently Smart Compiler (SBCL qualifies as one for this purpose), produces C++-like static dispatch for cases such as the one you're proposing. https://github.com/guicho271828/inlined-generic-function


It's not only about speed, it's also about deeply specializing templates.


But templates are about speed. Clojure-type dispatch can be easily implemented in CLOS (all of CLOS is just a set of Lisp macros and functions), and it's fully general. You can "deeply specialize" to your heart's content, the only issue being speed. As another comment mentioned, some compilers are smart enough to see through things that are known at compile time, or perhaps even JIT-specialze at runtime, but it's all about speed.


It's not only about speed. It's about dispatching on types that themselves are template instantiations. As an exercise, write a CLOS generic function that takes a list, and prints its elements forwards if the elements are ints, but prints them backwards if they are strings. You aren't allowed to use anything other than CLOS multimethods for type dispatch. In C++ this is super simple.


Why would you want to do that in Common Lisp?

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.


> Why would you want to do that in Common Lisp?

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.


You can implement this in Lisp. The dispatch will be at runtime, not compile time. I don't think CLOS supports this use case, but the rest of Lisp certainly does. The issue will be speed, as I mentioned before.


> You can implement this in Lisp. The dispatch will be at runtime, not compile time.

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)


> It's just not Lisp-y :-)

...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 dynamic, one can do that in CLOS+MOP with extensions like predicate dispatch or similar.

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.


C++ : Static. Compile-time method selection. But not dynamic.

CLOS: Dynamic dispatch is possible, given extensions like predicate dispatch.

ftp://publications.ai.mit.edu/ai-publications/2001/AITR-2001-006.pdf


No amount of runtime predicate dispatch is going to tell you if a particular occurrence of `nil` is intended to be used as a list of ints or as a list of strings, because runtime checks can't “see into the object's future”. On the other hand, a type checker can easily infer this, in some languages.


Type checker = static.


>Clojure-type dispatch can be easily implemented in CLOS

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.


Just an idea, most likely with macros.


Which is totally orthogonal to CLOS, though.


Didn't notice the part 4 at first. Initial thought was, "My god, how has this article been on the front page for two straight weeks?? Must be an incredible write-up."




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: