I am missing an important point about defmulti and defmethod. I've read several books' explanation of defmulti, and I'm still confused.
I want to get a random value depending on whether or not it's a transaction or an amount like 100.00
I want to call (random-val) and either get back an avail-trans value or random decimal amount. I have experimented with putting the functions in a map, but I get the same value back for avail-trans, a \B.
(def^:dynamic map-val {:trans (random-trans) :amt (random-amount)})
Here is the smallest amount of code to show what I'm doing that is not working. I'd appreciate any pointers or help.
(def^:dynamic avail-trans [\B \W \D \A])
(defn random-trans
[]
(nth avail-trans (.nextInt random (count avail-trans))))
(defn random-amount
[]
(float (/ (.nextInt random (count (range 1 10000))) 25 )))
The following is not constructed correctly, but I'm not sure why or how to fix the problem:
(defmulti random-val :val-type)
(defmethod random-val :trans []
(random-trans))
(defmethod random-val :amt []
(random-amount))
Calling (random-val :trans)
results in this error:
java.lang.IllegalArgumentException: No method in multimethod 'random-val' for dispatch value: null (NO_SOURCE_FILE:0)
A multimethod is created with defmulti
; you're doing that right. defmulti
needs a name and a dispatch function (and a docstring, plus some options, if you desire, but forget about those).
(defmulti random-val identity)
When you implement the multimethod with defmethod
, you need to specify the name of the multimethod you're implementing, the dispatch value it should match, and then the function tail (arglist plus whatever you want it to do).
(defmethod random-val :trans [t] (random-trans))
(defmethod random-val :amt [t] (random-amt))
You're getting java.lang.IllegalArgumentException: No method in multimethod 'random-val' for dispatch value: null (NO_SOURCE_FILE:0)
because when the dispatch function you assigned random-val
, :val-type
is applied to any other keyword, it gives you null
. When Clojure tries to look up a method to match that dispatch value, it fails.
But even if it didn't fail there, your defined methods have 0 arity (take no values), so you need to fix that, too (done above).
Finally, this doesn't seem like a good use for protocols. Just use your two separate function, random-amount
and random-trans
.
Note, too, that Clojure's website has a good explanations of multimethods.
You get the same value back every time for avail-trans '\B
' because you are evaluating the function when you associate it in your map map-val
, thus binding the value B
forever against the key ':trans' in map-val, instead of the function randon-trans
itself.
If you remove the parens around your function assignments in map-val, it will work just fine. Then there is no need for multimethods, which are probably not appropriate as @isaac-hodes suggests.
This works for me in the REPL:
(def avail-trans [\B \W \D \A])
(def random (java.util.Random.))
(defn random-trans []
(nth avail-trans (.nextInt random (count avail-trans))))
(defn random-amount []
(float (/ (.nextInt random (count (range 1 10000))) 25 )))
; No parens around function names
(def map-val {:trans random-trans :amt random-amount})
(println ((:trans map-val)))
(println ((:amt map-val)))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With