4

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)

2 Answers 2

7

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.

Sign up to request clarification or add additional context in comments.

3 Comments

I appreciate the answer very much. The two answers (so far) here are better than what I've seen out on Clojure's web site. No wise-guy intended, but Clojure seems to me to be one of those "It takes a village" languages/learning experiences. I get something from listening to all answers.
You've convinced me not to use this particular example for multimethods, but to experiment, if I re-arranged my example as you have shown, how would I call it?
I'm not quite sure what you're asking; you mean, how would you do it if you wanted to use multimethods? The code I wrote above should work as you intended.
2

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)))

1 Comment

Thanks, this was helpful. I was trying to peace out what was going wrong in the repl.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.