What are the benefits of using multimethods instead of cond in Clojure?
Asked Answered
S

1

9

Why shouldn't multimethods in Clojure simply be replaced by cond expressions?

I was inspired to ask after looking at the simple examples of multimethods in Ch. 5 of Russ Olsen's book Getting Clojure.

In a reply to a similar question (Performance of multimethod vs cond in Clojure), user Daniel Compton says

Multimethods allow for open extension; others can extend your multimethod dispatching on arbitrary expressions. Cond expressions are closed to extension by others or even your own code.

But it's not at all clear to me what "open extension" and "closed to extension" mean in this context, since it seems to me that both multimethods and cond expressions can be edited or expanded quite easily.

So... why shouldn't multimethods in Clojure simply be replaced by cond expressions?

Or, equivalently, how or when exactly can using multimethods be better or more elegant than using cond?

Schwenk answered 28/5, 2020 at 5:12 Comment(0)
B
8

The key point here is "allow for open extension". Anyone can add new branches for your multimethods - a cond is hard-coded: new dispatches must be added to the cond code in place.

Let's say: you have some widgets and you want to draw them. The widgets have a :type and you want to dispatch how to draw on that type.

Writing a big cond for all the widgets you know about will work. But now for each new widget, you have to touch your cond source code and modify it. This can be perfectly fine for e.g. an actual application, that does not need extension.

Doing the same with multimethods, anyone can implement a draw for their widget. So you don't know all of them when writing your code. This makes it a way better (or even mandatory) approach for e.g. libraries.

Now imagine, that you have decided for the cond approach in a library you are writing. Anyone with a new widget now must write their own draw, dispatch for their draws first, then call your draw. Also they would have to make sure, their draw is called everywhere, where your draw was called to have it working (which often is just impossible in a clean way).

One popular example for a multimethod directly in the Clojure core is print-method. By that anyone can implement a "serialization" for their type and play nicely.

Other honourable mentions for examples to look at are clojure.test and integrant.

Berke answered 28/5, 2020 at 5:22 Comment(5)
But, in order to extend a multimethod, wouldn't one still need to edit the source code of the multimethod's dispatch function? If so, wouldn't that be just as much work as editing the source code of a cond?Schwenk
The defmulti decides how to dispatch, and if you want to take part, your type has to conform how the dispatch is done. If you have to change the way, the dispatch is done, or have to change the action for a certain type, then of course you have to touch the original code. cond decides AND dispatches while the defmulti only extracts the how.Berke
For a concrete example, suppose that your multimethod's dispatch function is :type. I can add a new branch for it without editing the dispatch function: simply (defmulti foo :mytype ...), and then call your multimethod with objects like {:type :mytype :size 10}. This is how Clojure's printing facility works.Farmyard
I was going to use print-method as an example, but you beat me to it! Doh!Typist
This follows the open/closed principle: "software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification"; that is, such an entity can allow its behaviour to be extended without modifying its source code.Briefs

© 2022 - 2024 — McMap. All rights reserved.