Building Clojure defmulti / defmethod

I am missing an important question about defmulti and defmethod. I read a few explanations of the defmulti book and I'm still confused.

I want to get a random value depending on whether it is a transaction or an amount equal to 100.00

I want to call (random-val) and either return the value of avail-trans, or a random decimal sum. I experimented with placing functions on a map, but I get the same value for avail-trans, a \ B.

(def^:dynamic map-val {:trans (random-trans) :amt (random-amount)}) 

Here is the smallest code to show that I am doing what is not working. I would appreciate any guidance 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 ))) 

Not built correctly, but I'm not sure why and how to solve the problem:

 (defmulti random-val :val-type) (defmethod random-val :trans [] (random-trans)) (defmethod random-val :amt [] (random-amount)) 

A call (random-val :trans) results in an error:

java.lang.IllegalArgumentException: There is no method in multimethod 'random-val' for the send value: null (NO_SOURCE_FILE: 0)

+4
source share
2 answers

Multimedia is created using defmulti ; you are doing it right. defmulti requires a name and a submit function (and a docstring, as well as some parameters if you want, but forget about it).

 (defmulti random-val identity) 

When you implement a multimethod using defmethod , you need to specify the name of the multimethod you are using, the send value that it should match, and then the tail of the function (arglist plus whatever you want).

 (defmethod random-val :trans [t] (random-trans)) (defmethod random-val :amt [t] (random-amt)) 

You get java.lang.IllegalArgumentException: No method in multimethod 'random-val' for dispatch value: null (NO_SOURCE_FILE:0) , because when the send function you assigned random-val :val-type applies to any other keyword, it gives you null . When Clojure tries to find a method that matches this send value, it fails.

But even if this does not happen, your specific methods have 0 arity (do not take values), so you also need to fix it (done above).

Lastly, this is not like using protocols. Just use two different functions: random-amount and random-trans .

Note that the Clojure website has good multi-point explanations.

+7
source

You get the same value every time for avail-trans' \B ', because you evaluate the function when you associate it with your map-val , thereby binding the value of B forever to the key: trans' in map-val instead of the randon-trans function randon-trans .

If you remove paranas around function assignments in map-val, this will work fine. Then there is no need for multimethods that are probably not suitable, as @ isaac-hodes suggests.

This works for me in 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
source

Source: https://habr.com/ru/post/1386832/


All Articles