Reputation: 27626
Suppose I have a closed world of valid dispatch keys; in my concrete example, it's the type of nybbles. There are two obvious ways to define an operation of some parameters that behaves differently based on a nybble argument:
Using case
, e.g.:
(defn read-arg [arg-mode mem cursor]
(case arg-mode
0x0 [:imm 0]
0x1 [:imm (read-fwd peek-word8 mem cursor)]
;; And so on
0xf [:ram (read-fwd peek-word32 mem cursor)]))
Using defmulti
/defmethod
:
(defmulti read-arg (fn [arg-mode mem cursor] arg-mode))
(defmethod read-arg 0x0 [_ mem cursor] [:imm 0])
(defmethod read-arg 0x1 [_ mem cursor] [:imm (read-fwd peek-word8 mem cursor)])
;; And so on
(defmethod read-arg 0xf [_ mem cursor] [:ram (read-fwd peek-word32 mem cursor)])
Which is considered nicer style for Clojure? Would the answer be different if the dispatch was done on symbols instead of nybbles?
Upvotes: 3
Views: 91
Reputation: 84331
I would expect the dispatch itself to be so much faster in this case using case
as to render any stylistic considerations moot – dispatching based on an integer value in a narrow range is the best case for case
, really – and then I actually think that case
is also better style (this really does look like closed-world dispatch, so it's good to use a mechanism that signals that).
As a further comment, it seems like the case
"result expressions" will be fairly short here, which is good. If not, then it might be worth factoring them out into their own functions to prevent the method performing the dispatch from growing too large. (The JIT will then be able to inline just those portions it finds are worth inlining.)
As for whether I'd feel different if symbols where involved, that depends on a number of factors – performance (is it important? is there a big difference in benchmarks?), number and size of branches (it's good to keep overall method size small – for performance and readability reasons – so the ability to define each branch in a separate defmethod
can be convenient) etc.
Upvotes: 2