Clojure multimethods vs. protocols
Asked Answered
H

4

45

I'm a Clojure novice and was looking for some concrete examples of when to use protocols and when to use multimethods. I know that protocols are generally geared towards creating a type hierarchy and typical OOP things, that they were added to the language after multimethods, and that protocols generally have better performance so my question is this:

Are protocols meant to replace multimethods? If not, could you give me an example where I would use multimethods instead of protocols?

Harvison answered 9/11, 2011 at 19:21 Comment(0)
Z
35

Multimethods are more powerful and more expensive,

use protocols when they are sufficient but if you need to dispatch based on the phase of the moon as seen from mars then multimethods are your best choice.

Protocols exist to allow the simple stuff to stay simple and provide a way for clojure to generate very much the same bytecode that the equivalent java would. It seems that most people use protocols most of the time. I use multimethods when I need to dispatch on more than one argument, though I have to admit that this has only come up once, and full isa hierarchies are used even less often (by me). so in short use Multimethods when you need them

the best example In my expierence is right at the start, in core.clj

Zanezaneski answered 9/11, 2011 at 19:24 Comment(3)
Protocols force you to give up a large level of dynamicity. They cause very confusing behaviour in development when reloading and lock you into dispatching only on the type of the first argument, which makes it very difficult to change your code down the line. Protocols are emphatically not simple; they're a way to hook into the low-level dispatch of the JVM in order to get speed necessary for self-hosting. Multimethods are simple--they're just vars with dispatch functions.Wassyngton
On the other hand, if you are a Clojure newbie and you think you need polymorphism, there's a good chance you're wrong. A genuine need for defining new polymorphic functionality is very rare in Clojure.Wassyngton
@technomancy, can you please elaborate on this? For a clojure newbie who is used thinking and working in a certain way it seems hard to do certain tasks without polymorphism. Can you think of any examples which illustrate idiomatic clojure over polymorphism?Radicalism
B
38

Protocol and multimethods are complementary and intended for slightly different use cases.

  • Protocols provide efficient polymorphic dispatch based on the type of the first argument. Because the is able to exploit some very efficient JVM features, protocols give you the best performance.
  • Multimethods enable very flexible polymorphism which can dispatch based on any function of the method's arguments. Multimethods are slower but very flexible

In general, my advice is to use protocols unless you have a specific case that requires multimethods.

A case where you could require multimethods is something like the following:

(defn balance-available? [amount balance] (> balance amount))

(defmulti withdraw balance-available?)

(defmethod withdraw true [amount balance] 
  (- balance amount))

(defmethod withdraw false [amount balance] 
  (throw (Error. "Insufficient balance available!")))

Note that you can't use protocols here for both of the following reasons:

  • The dispatch function needs to use both arguments to determine which method implementation to use (i.e. it is a multiple dispatch case).
  • You also can't distinguish on the type of the first argument (which is presumably always a numeric value)
Bluepoint answered 10/11, 2011 at 3:14 Comment(0)
Z
35

Multimethods are more powerful and more expensive,

use protocols when they are sufficient but if you need to dispatch based on the phase of the moon as seen from mars then multimethods are your best choice.

Protocols exist to allow the simple stuff to stay simple and provide a way for clojure to generate very much the same bytecode that the equivalent java would. It seems that most people use protocols most of the time. I use multimethods when I need to dispatch on more than one argument, though I have to admit that this has only come up once, and full isa hierarchies are used even less often (by me). so in short use Multimethods when you need them

the best example In my expierence is right at the start, in core.clj

Zanezaneski answered 9/11, 2011 at 19:24 Comment(3)
Protocols force you to give up a large level of dynamicity. They cause very confusing behaviour in development when reloading and lock you into dispatching only on the type of the first argument, which makes it very difficult to change your code down the line. Protocols are emphatically not simple; they're a way to hook into the low-level dispatch of the JVM in order to get speed necessary for self-hosting. Multimethods are simple--they're just vars with dispatch functions.Wassyngton
On the other hand, if you are a Clojure newbie and you think you need polymorphism, there's a good chance you're wrong. A genuine need for defining new polymorphic functionality is very rare in Clojure.Wassyngton
@technomancy, can you please elaborate on this? For a clojure newbie who is used thinking and working in a certain way it seems hard to do certain tasks without polymorphism. Can you think of any examples which illustrate idiomatic clojure over polymorphism?Radicalism
D
7

As mention by Arthur, multimethods are more powerful and more expensive. Indeed, protocols can be thought of as a special case of mutlimethods where the dispatch function is class. Of course, this is not really the case as protocols are more than that.

If you need to dispatch on something other than the class of the first argument, you'll need to use a multimethod, or redesign. Dispatching on type is a good use case for protocols.

Doglike answered 9/11, 2011 at 22:0 Comment(0)
C
3

I like multimethods when you don't otherwise need a class hierarchy. For example if you have a media database and your records are like {:media-type :video, :bytes ...} then you can have a multimethod

(defmulti make-grayscale :media-type)

Then you can make various

; in video.clj
(defmethod make-grayscale :video [record]
  (ffmpeg ... (:bytes record))

; in photo.clj
(defmethod make-grayscale :photo [record]
  (imagemagick ... (:bytes record))

That way you can avoid having a central cond expression, so you get the modularity of classes. But you don't have to go through all that "wrapper class hierarchy" boilerplate, which to me is a bane that should be left for the Java world. Multimethods are just functions and feel more clojuresque to me.

Chromogenic answered 13/1, 2015 at 16:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.