I am working on the multimethod chapter this week. This has required a
lot of exploration, as the multimethod feature set goes well beyond
what most people are using yet. I have hit one rough spot: derive. I
have working code (below), but I don't like the way I have to call it
with fully qualified keywords, e.g.
(service-charge {:tag :examples.multimethods.service-charge-3/
checking :balance 100})
-> 25
(service-charge {:tag :examples.multimethods.service-charge-3/
checking :balance 10000})
-> 0
I feel that I have made a wrong turn somewhere. Here are my assumptions:
1. I (the implementer) have to write my dispatch functions with
qualified names, if I want to use derive.
2. John Doe (the caller) must use fully qualified names *everywhere*.
Since he does not live in my namespace he cannot use the ::.
It's the latter that bothers me. It seems so ugly that I would never
use hierarchical names for anything, which makes me think I am missing
something. To make matters worse:
3. Once I use :: once on any keyword in my implementation, it is a
quick slope to using it other places too, just so I don't have to
remember which ones I chose to qualify and which ones I didn't. In the
code below, :premium and :basic become ::premium and ::basic just for
consistency with ::checking and ::savings.
Is anybody else working with derive? What are your experiences?
Thanks,
Stu
---------------------------------------------------------------------------
(ns examples.multimethods.service-charge-3)
(defmulti account-level :tag)
(println ::checking)
(defmethod account-level ::checking [acct]
(if (>= (:balance acct) 5000) ::premium ::basic))
(defmethod account-level ::savings [acct]
(if (>= (:balance acct) 1000) ::premium ::basic))
(derive ::savings ::account)
(derive ::checking ::account)
(defmulti service-charge (fn [acct] [(account-level acct) (:tag acct)]))
(defmethod service-charge [::basic ::checking] [_] 25)
(defmethod service-charge [::basic ::savings] [_] 10)
(defmethod service-charge [::premium ::account] [_] 0)
Very helpful, as always. Alias + the ability to pull in symbols names
via refer was exactly what I was looking for.
One scenario still worries me:
1. I create a multimethod that dispatches around a tag whose value is
an unresolved keyword (:Foo instead of ::Foo). Everything works fine.
2. If at some later point I want the dispatch to depend on (derive),
it is breaking change for clients to switch to from :Foo to ::Foo.
I am tempted to conclude that you should never use unqualified
keywords as type tags, because you are exposing an implementation
detail. That is, the implementation promises not to rely on derive.
Cheers,
Stuart