I am trying to write a multi-method which dispatches based on the types of all the arguments passed to it, but I am struggling to figure out how to write such a distpatch fn.
By that I mean, given:
(defmulti foo (fn [& args] ...))
(defmethod foo String
[& args]
(println "All strings"))
(defmethod foo Long
[& args]
(println "All longs"))
(defmethod foo Number
[& args]
(println "All numbers"))
(defmethod foo :default
[& args]
(println "Default"))
Then we would get:
(foo "foo" "bar" "baz") => "All strings"
(foo 10 20 30) => "All longs"
(foo 0.5 10 2/3) => "All numbers"
(foo "foo" 10 #{:a 1 :b 2}) => "Default"
(defmulti foo (fn [& args] (into #{} (map class args))))
(defmethod foo #{String}
[& args]
(println "All strings"))
(defmethod foo #{Long}
[& args]
(println "All longs"))
(defmethod foo #{Number}
[& args]
(println "All numbers"))
(defmethod foo :default
[& args]
(println "Default"))
Results in:
(foo "foo" "bar" "baz") => "All strings"
(foo 10 20 30) => "All longs"
(foo 0.5 10 2/3) => "Default"
(foo "foo" 10 #{:a 1 :b 2}) => "Default"
So the Number example doesn't work.
Here is the dispatcher fn:
(defmulti foo (fn [& args]
(let [m (distinct (map type args))]
(when (= 1 (count m))
(first m)))))
Related
For example
(def inc-map (let [inum (atom 0)]
{:countup (fn[](swap! inum inc))
:get (fn[](#inum))}))
((inc-map :countup )) ;increase inside value
; ⇒ 1
((inc-map :get)) ;get current value
; ⇒ 1
Can I get access inum when I want to add more functions later?
E.g. I want to do this:
(def inc-map
(assoc inc-map :countdown (fn[] ???)))
How can I access inum at ????
it could be possible, if you expose one more function, say :update, enclosing the inum value.
(def inc-map (let [inum (atom 0)]
{:update (fn [f & args] (apply swap! inum f args))
:countup (fn [] (swap! inum inc))
:get (fn [] #inum)}))
user> (def inc-dec-map (assoc inc-map :countdown
(fn [] ((inc-map :update) dec))))
#'user/inc-dec-map
user> ((inc-dec-map :countup))
;;=> 1
user> ((inc-dec-map :countup))
;;=> 2
user> ((inc-dec-map :countdown))
;;=> 1
user> ((inc-dec-map :countdown))
;;=> 0
user> ((inc-dec-map :countdown))
;;=> -1
and then you can just seal it, dissoc'ing :update, (say if you make it publicly accessed to some other namespace.
otherwise you could provide an getter/updater to the ops object:
(def counter-ops (let [inum (atom 0)
ops (atom {:countup (fn [] (swap! inum inc))
:get (fn [] #inum)})]
(fn
;; get operations map snapshot
([] #ops)
;; get operation
([op] (-> ops deref op))
;; set operation
([op f & args] (swap! ops assoc op (fn [& args] (apply f inum args)))))))
user> (counter-ops :countdown (fn [inum] (swap! inum dec)))
user> (counter-ops :decrease-by (fn [inum n] (swap! inum - n)))
user> ((counter-ops :countdown))
;;=> -1
user> ((counter-ops :countdown))
;;=> -2
user> ((counter-ops :countup))
;;=> -1
user> ((counter-ops :countup))
;;=> 0
user> ((counter-ops :decrease-by) 10)
;;=> -10
seal it to be locked for any subsequent op additions:
user> (def counter-ops-sealed (counter-ops))
#'user/counter-ops-sealed
user> ((counter-ops-sealed :countup))
;;=> 2
user> ((counter-ops-sealed :countdown))
;;=> 1
I would like to be able to mock MyFunction however I need the mock to return different values when MyFunction is called.
Is it possible to use with-redefs to return different values based on the call order of a function?
(testing "POST /foo/bar and return ok"
(with-redefs [->Baz (fn [_]
(reify MyProtocol (MyFunction [_] [{:something 1}]))
(reify MyProtocol (MyFunction [_] [{:something 2}])))]
(let [response (routes/foo {:request-method :post
:uri "/foo/bar"
:query-params {}
})]
(is (= (:status response) 200)))))
You could use a mutable collection of the return values, then return/remove values from it on each call.
(defn foo [x] (inc x)) ;; example fn to be mocked
If you wanted to mock three calls to foo returning 1, 2, and 3 respectively:
(with-redefs [foo (let [results (atom [1 2 3])]
(fn [_] (ffirst (swap-vals! results rest))))]
(prn (foo 0))
(prn (foo 0))
(prn (foo 0))
;; additional calls would return nil
(prn (foo 0)))
;; 1
;; 2
;; 3
;; nil
That uses swap-vals! to get the old/new values of the atom, but requires Clojure 1.9 or greater.
If you don't have swap-vals! you could do it (less atomically) like this:
(with-redefs [foo (let [results (atom [1 2 3])]
(fn [_]
(let [result (first #results)]
(swap! results rest)
result)))]
...)
We use Picomock for this, and to assert on the parameters for each call, and to assert on the number of calls. Recommended!
I am researching how to use graphql in clojure and it all seems to make sense, and I can follow the Demo Project, except for the resolver-fn
In https://github.com/tendant/graphql-clj the resolver function is defined to be
(defn resolver-fn [type-name field-name]
(cond
(and (= "QueryRoot" type-name) (= "user" field-name)) (fn [context parent args]
{:name "test user name"
:age 30})))
And in the demo, https://github.com/tendant/graphql-clj-starter, the resolver function is
(defn starter-resolver-fn [type-name field-name]
(match/match
[type-name field-name]
["Query" "hero"] (fn [context parent args]
(get-hero (:episode args)))
["Query" "human"] (fn [context parent args]
(get-human (str (get args "id"))))
["Query" "droid"] (fn [context parent args]
(get-droid (str (get args "id"))))
["Query" "objectList"] (fn [context parent args]
(repeat 3 {:id (java.util.UUID/randomUUID)}))
;; Hacky!!! Should use resolver for interface
["Human" "friends"] (fn [context parent args]
(get-friends parent))
["Droid" "friends"] (fn [context parent args]
(get-friends parent))
["Character" "friends"] (fn [context parent args]
(get-friends parent))
["Mutation" "createHuman"] (fn [context parent args]
(create-human args))
:else nil))
I am confused about what arguments the resolver-fn should take.
I see that it returns another function but where do the context, parent and args come from for this function.
Any help would be much appreciated
I have a namespace like this:
(ns foo.core)
(def ^:dynamic *debug-fn*
"A function taking arguments [bar baz]"
nil)
(defn bar-info
[bar _]
(println bar))
(defn baz-info
[_ baz]
(println baz))
(defn do-stuff
[bar baz]
(when *debug-fn* (*debug-fn* bar baz)))
(defn -main
[& {:keys [debug-fn]}]
(binding [*debug-fn* (symbol debug-fn)] ;; THIS WON'T WORK!
(do-stuff 27 42)))
What I would like to do is allow a debug function to be specified from the command line like this: lein run bar-info or lein run baz-info.
I'm not sure how to take the string specified as a command-line argument and turn it into the namespace-qualified function to bind. Do I need a macro to do this?
Use ns-resolve, you will need to specify namespace where your function is defined though.
user=> (defn f [n] (* n n n))
#'user/f
user=> ((ns-resolve *ns* (symbol "f")) 10)
1000
Use alter-var-root:
user=> (doc alter-var-root)
-------------------------
clojure.core/alter-var-root
([v f & args])
Atomically alters the root binding of var v by applying f to its
current value plus any args
nil
user=> (alter-var-root #'*debug-fn* (fn [v] (fn [x] (println x) x)))
#<user$eval171$fn__172$fn__173 user$eval171$fn__172$fn__173#7c93d88e>
user=> (*debug-fn* 1)
1
1
Though I've accepted Guillermo's answer above, I figured that it might also be useful to add the solution I ended up going with:
(def debug-fns
{:bar-info (fn [bar _] (println bar))
:baz-info (fn [_ baz] (println baz))
(def active-debug-fns (atom []))
(defn activate-debug-fn!
[fn-key]
(let [f (debug-fns fn-key)]
(if f
(swap! active-debug-fns conj f)
(warn (str "Debug function " fn-key " not found! Available functions are: "
(join " " (map name (keys debug-fns))))))))
(defn debug-fn-keys
[args]
(if (args "--debug")
(split (or (args "--debug") "") #",")
[]))
(defn do-stuff
[bar baz]
(doseq [f #active-debug-fns]
(f bar baz)))
(defn -main
[& args]
(let [args (apply hash-map args)]
(doseq [f (debug-fn-keys args)]
(activate-debug-fn! (keyword k)))
(do-stuff 27 42)))
So now you can say something like lein run --debug bar-info to get info on bars, or lein run --debug bar,baz to get info on both bars and bazes.
Any suggestions to make this more idiomatic will be happily accepted and edited in. :)
The following code appears to force line-seq to read 4 lines from file. Is this some kind of buffering mechanism? Do I need to use lazy-cat here? If so, how can I apply a macro to a sequence as if it were variadic arguments?
(defn char-seq [rdr]
(let [coll (line-seq rdr)]
(apply concat (map (fn [x] (println \,) x) coll))))
(def tmp (char-seq (clojure.contrib.io/reader file)))
;,
;,
;,
;,
#'user/tmp
Part of what you're seeing is due to apply, since it will need to realize as many args as needed by the function definition. E.g.:
user=> (defn foo [& args] nil)
#'user/foo
user=> (def bar (apply foo (iterate #(let [i (inc %)] (println i) i) 0)))
1
#'user/bar
user=> (defn foo [x & args] nil)
#'user/foo
user=> (def bar (apply foo (iterate #(let [i (inc %)] (println i) i) 0)))
1
2
#'user/bar
user=> (defn foo [x y & args] nil)
#'user/foo
user=> (def bar (apply foo (iterate #(let [i (inc %)] (println i) i) 0)))
1
2
3
#'user/bar