Understanding multimethods dispatching - clojure

I can understand how this works:
(defmulti area :Shape)
(defmethod area :B [x] (println "Rect"))
(defmethod area :C [x] (println "Circle"))
(defmethod area :default [x] (do (println "Unknown") (:Shape x)))
(area {:Shape :B})
=> Rect
nil
(area {:Shape :Bb})
=> Unknown
:Bb
But by simply changing :Shape to :Shap, I stop understanding how this is being dispatched:
(defmulti area :Shap)
(defmethod area :B [x] (println "Rect"))
(defmethod area :C [x] (println "Circle"))
(defmethod area :default [x] (do (println "Unknown") (:Shap x)))
(area {:Shap :B})
=> Unknown
:B
(area {:Shap :C})
=> Unknown
:C
Clojure 1.5.1 on Eclipse with Counterclockwise plugin

Multimethods don't like being redefined with a new dispatch function. This is a rather controversial feature, but it's the way things are. In order to change a multimethod's dispatch function, you must first def it to something which is not a multimethod (for example, to nil).
(defmulti area :Shape)
(def area nil)
(defmulti area :Shap)

Related

ClojureScript map destructure with defaults filled in?

Does Clojure/Script offer a way to build a destructured map out of the arguments plus filled-in defaults in case the keys weren't supplied in the call?
Consider this example (that doesn't quite do what the code implies by a quick glance). Does clojure provide a way to build the map prompt with these four keys and values either from the caller or the defaults. I hate to think I have to repeat these key names two more times to get what I am after.
(re-frame/reg-event-db
:open-prompt
(fn [db [_ {title :title
text :text
label-yes :label-yes
label-no :label-no
:or {title "Confirm"
text "Are you sure?"
label-yes "Ok"
label-no "Cancel"}
:as prompt}]]
(-> db
(update :state conj :modal-prompt)
(assoc :prompt prompt))))
After reviewing the official documentation page about destructuring, I don't think that Clojure proposes a more convient way of doing that.
But just by curiosity, I was wondering what is the code generated by destructuring, because I'm expecting it relies on macro stuff. Let consider this toy example:
(def my-map {:text "Some text"})
(let
[{title :title
:or {title "Confirm"}
:as prompt} my-map]
(str "I got " title " from " prompt))
;; => "I got Confirm from {:text \"Some text\"}"
(macroexpand '(let
[{title :title
:or {title "Confirm"}
:as prompt} my-map]
(str "I got " title " from " prompt)))
;; => (let*
;; [map__12555
;; my-map
;; map__12555
;; (if
;; (clojure.core/seq? map__12555)
;; (clojure.lang.PersistentHashMap/create
;; (clojure.core/seq map__12555))
;; map__12555)
;; prompt
;; map__12555
;; title
;; (clojure.core/get map__12555 :title "Confirm")]
;; (str "I got " title " from " prompt))
So as you can see, after a macro expansion, the :or mechanism which allows to specifies default value relies on clojure.core/get.
In this particular example, title is affected by (clojure.core/get map__12555 :title "Confirm") form. It's a way to avoid repeating the title variable, but does it worth it?
You can also check the source code of the destructuring macro to get full details about it, but personally I found it pretty difficult to handle ^^'.
it is doable, maybe not very practical though, but nice for self education:
let's begin with making up the function what would be special binding case.
let's say, we want to pass vectors of length 2 or 3, where vector of 2 will represent the simple binding map key-value pair like [:as abc] or [a :a], and the vector of size 3 would be k-v-default triple: [a :a "my default"]. The example of it's usage:
(bindings-preproc [['a 1 "asd"]
['b 2 "ddd"]
[:or {'x 10}]
[:as 'whole]])
resulting to
{a 1, b 2, :or {x 10, a "asd", b "ddd"}, :as whole}
this function could look like this:
(defn bindings-preproc [decls]
(let [defaults (into {} (keep (fn [decl]
(when (and (not (keyword? (first decl)))
(= 3 (count decl)))
(let [[nm _ default] decl]
[nm default])))
decls))
all-kvs (apply assoc {} (mapcat (partial take 2) decls))]
(update all-kvs :or merge defaults)))
(this one doesn't include error checks for the sake of illustrative simplicity)
The next thing is to employ it inside the binding macros. The idea to make bindings-preproc a macro fails, because binding forms are checked for validity before the inner macros are evaluated.
But still we have a feature, that would help, namely reader tags. They are used for example when you use #inst syntax. Since these reader tags are processed at read-time, before any macros are getting expanded, we can plug our preprocessor in.
(here i will use actual reference update, to demonstrate it from repl, but in real projects you would declare these tags in a special file)
user> (alter-var-root
#'default-data-readers
assoc 'my/reader #'user/bindings-preproc)
;;=> {uuid #'clojure.uuid/default-uuid-reader,
;; inst #'clojure.instant/read-instant-date,
;; my/reader #'user/bindings-preproc}
so, now we can try to make it work:
(defn f [#my/reader [[a :a 10]
[b :b 20]
[z :z]
[:keys [k1 k2 k3]]
[[c1 c2 & cs] :c]
[:or {z 101
k3 :wooo}]
[:as whole]]]
{:a a :b b :c1 c1 :c2 c2 :cs cs :z z :k1 k1 :k2 k2 :k3 k3 :whole whole})
user> (f {:a 1000 :c [:one]})
;;=> {:cs nil,
;; :c2 nil,
;; :z 101,
;; :c1 :one,
;; :k3 :wooo,
;; :b 20,
;; :whole {:a 1000, :c [:one]},
;; :k1 nil,
;; :k2 nil,
;; :a 1000}
user> (let [a 10
b 20
#my/reader [[x :x 1]
[y :y 2]
[z :z 100]] {:z 432}]
[a b x y z])
;;=> [10 20 1 2 432]
I like to make a map of all default values, then use into or similar to fuse the user-supplied values into the map of default values. For example:
(ns tst.demo.core
(:use tupelo.core tupelo.test) )
(def stuff-default {:a 1 :b 2})
(defn apply-defaults
[arg]
(let [stuff (glue stuff-default arg)] ; or use `into`. Last one wins, so put defaults first
(with-map-vals stuff [a b]
(newline)
(spyx a)
(spyx b))
stuff))
(dotest
(is= (apply-defaults {}) ; no inputs => all default values
{:a 1, :b 2})
(is= (apply-defaults {:a 100}) ; some inputs => partial defaults
{:a 100, :b 2})
(is= (apply-defaults {:a 100, :b 200}) ; all inputs => no defaults used
{:a 100, :b 200}))
Here glue is like into but with more error checking. We also use tupelo.core/with-map-vals to destruct the map, with less repetition than native Clojure destructuring (vals->map does the reverse).
The output is:
-------------------------------
Clojure 1.10.1 Java 14
-------------------------------
a => 1
b => 2
a => 100
b => 2
a => 100
b => 200
Ran 2 tests containing 3 assertions.
0 failures, 0 errors.

Howto include clojure.spec'd functions in a test suite

Is there anyway to include clojure.spec'd functions in a generalized test suite? I know we can register specs and directly spec functions.
(ns foo
(:require [clojure.spec :as s]
[clojure.spec.test :as stest]))
(defn average [list-sum list-count]
(/ list-sum list-count))
(s/fdef average
:args (s/and (s/cat :list-sum float? :list-count integer?)
#(not (zero? (:list-count %))))
:ret number?)
And later, if I want to run generative tests against that spec'd function, I can use stest/check.
=> (stest/check `average)
({:spec #object[clojure.spec$fspec_impl$reify__14282 0x68e9f37c "clojure.spec$fspec_impl$reify__14282#68e9f37c"], :clojure.spec.test.check/ret {:result true, :num-tests 1000, :seed 1479587517232}, :sym edgar.core.analysis.lagging/average})
But i) is there anyway to include these test runs in my general test suite? I'm thinking of the kind of clojure.test integration that test.check has. The closest thing that I can see ii) is the stest/instrument (see here) function. But that seems to just let us turn on checking at the repl. Not quite what I want. Also, iii) are function specs registered?
(defspec foo-test
100
;; NOT this
#_(prop/for-all [v ...]
(= v ...))
;; but THIS
(stest/some-unknown-spec-fn foo))
Ok, solved this one. Turns out there's no solution out of the box. But some people on the clojure-spec slack channel have put together a defspec-test solution for clojure.spec.test and clojure.test.
So given the code in the question. You can A) define the defspec-test macro that takes your test name and a list of spec'd functions. You can then B) use it in your test suite.
Thanks Clojure community!! And hopefully such a utility function makes it into the core library.
A)
(ns foo.test
(:require [clojure.test :as t]
[clojure.string :as str]))
(defmacro defspec-test
([name sym-or-syms] `(defspec-test ~name ~sym-or-syms nil))
([name sym-or-syms opts]
(when t/*load-tests*
`(def ~(vary-meta name assoc
:test `(fn []
(let [check-results# (clojure.spec.test/check ~sym-or-syms ~opts)
checks-passed?# (every? nil? (map :failure check-results#))]
(if checks-passed?#
(t/do-report {:type :pass
:message (str "Generative tests pass for "
(str/join ", " (map :sym check-results#)))})
(doseq [failed-check# (filter :failure check-results#)
:let [r# (clojure.spec.test/abbrev-result failed-check#)
failure# (:failure r#)]]
(t/do-report
{:type :fail
:message (with-out-str (clojure.spec/explain-out failure#))
:expected (->> r# :spec rest (apply hash-map) :ret)
:actual (if (instance? Throwable failure#)
failure#
(:clojure.spec.test/val failure#))})))
checks-passed?#)))
(fn [] (t/test-var (var ~name)))))))
B)
(ns foo-test
(:require [foo.test :refer [defspec-test]]
[foo]))
(defspec-test test-average [foo/average])
The above example can fail in the case where :failure is false due to how stest/abbrev-result tests for failure. See CLJ-2246 for more details. You can work around this by defining your own version of abbrev-result. Also, the formatting of failure data has changed.
(require
'[clojure.string :as str]
'[clojure.test :as test]
'[clojure.spec.alpha :as s]
'[clojure.spec.test.alpha :as stest])
;; extracted from clojure.spec.test.alpha
(defn failure-type [x] (::s/failure (ex-data x)))
(defn unwrap-failure [x] (if (failure-type x) (ex-data x) x))
(defn failure? [{:keys [:failure]}] (not (or (true? failure) (nil? failure))))
;; modified from clojure.spec.test.alpha
(defn abbrev-result [x]
(let [failure (:failure x)]
(if (failure? x)
(-> (dissoc x ::stc/ret)
(update :spec s/describe)
(update :failure unwrap-failure))
(dissoc x :spec ::stc/ret))))
(defn throwable? [x]
(instance? Throwable x))
(defn failure-report [failure]
(let [expected (->> (abbrev-result failure) :spec rest (apply hash-map) :ret)]
(if (throwable? failure)
{:type :error
:message "Exception thrown in check"
:expected expected
:actual failure}
(let [data (ex-data (get-in failure
[::stc/ret
:result-data
:clojure.test.check.properties/error]))]
{:type :fail
:message (with-out-str (s/explain-out data))
:expected expected
:actual (::s/value data)}))))
(defn check?
[msg [_ body :as form]]
`(let [results# ~body
failures# (filter failure? results#)]
(if (empty? failures#)
[{:type :pass
:message (str "Generative tests pass for "
(str/join ", " (map :sym results#)))}]
(map failure-report failures#))))
(defmethod test/assert-expr 'check?
[msg form]
`(dorun (map test/do-report ~(check? msg form))))
Here's a slightly modified version of grzm's excellent answer that works with [org.clojure/test.check "0.10.0-alpha4"]. It uses the new :pass? key that comes from this PR: https://github.com/clojure/test.check/commit/09927b64a60c8bfbffe2e4a88d76ee4046eef1bc#diff-5eb045ad9cf20dd057f8344a877abd89R1184.
(:require [clojure.test :as t]
[clojure.string :as str]
[clojure.spec.alpha :as s]
[clojure.spec.test.alpha :as stest])
(alias 'stc 'clojure.spec.test.check)
;; extracted from clojure.spec.test.alpha
(defn failure-type [x] (::s/failure (ex-data x)))
(defn unwrap-failure [x] (if (failure-type x) (ex-data x) x))
;; modified from clojure.spec.test.alpha
(defn abbrev-result [x]
(if (-> x :stc/ret :pass?)
(dissoc x :spec ::stc/ret)
(-> (dissoc x ::stc/ret)
(update :spec s/describe)
(update :failure unwrap-failure))))
(defn throwable? [x]
(instance? Throwable x))
(defn failure-report [failure]
(let [abbrev (abbrev-result failure)
expected (->> abbrev :spec rest (apply hash-map) :ret)
reason (:failure abbrev)]
(if (throwable? reason)
{:type :error
:message "Exception thrown in check"
:expected expected
:actual reason}
(let [data (ex-data (get-in failure
[::stc/ret
:shrunk
:result-data
:clojure.test.check.properties/error]))]
{:type :fail
:message (with-out-str (s/explain-out data))
:expected expected
:actual (::s/value data)}))))
(defn check?
[msg [_ body :as form]]
`(let [results# ~body
failures# (remove (comp :pass? ::stc/ret) results#)]
(if (empty? failures#)
[{:type :pass
:message (str "Generative tests pass for "
(str/join ", " (map :sym results#)))}]
(map failure-report failures#))))
(defmethod t/assert-expr 'check?
[msg form]
`(dorun (map t/do-report ~(check? msg form))))
Usage:
(deftest whatever-test
(is (check? (stest/check `whatever
;; optional
{:clojure.spec.test.check/opts {:num-tests 10000}})))

How to apply a function to a range in Clojure?

I'm new to Clojure and I tried to play with the example data from clojuredocs.org.
;; Data
(def scenes [{:subject "Frankie"
:action "say"
:object "relax"}
{:subject "Lucy"
:action "loves"
:object "Clojure"}
{:subject "Rich"
:action "tries"
:object "a new conditioner"}])
(defn play [scenes n]
"Play a scene"
(->>
scenes
(#(get % n))
((juxt :subject :action :object))
(interpose " ")
(apply str)))
The play function works fine:
my-stuff.core> (play scenes 0)
"Frankie say relax"
my-stuff.core> (play scenes 1)
"Lucy loves Clojure"
my-stuff.core> (play scenes 2)
"Rich tries a new conditioner"
This play-all function doesn't work:
(defn play-all [scenes]
"Play all the scenes"
(let [x (count scenes)]
(for [n (range x)]
(map play scenes n ))))
How to correct this play-all function, i.e. how to apply the play function to the range of data?
You don't need both for and map.
With only for:
user=> (defn play-all [scenes]
#_=> "Play all the scenes"
#_=> (let [x (count scenes)]
#_=> (for [n (range x)]
#_=> (play scenes n ))))
#'user/play-all
user=> (play-all scenes)
("Frankie say relax" "Lucy loves Clojure" "Rich tries a new conditioner")
and with only map:
user=> (defn play-all [scenes]
#_=> "Play all the scenes"
#_=> (let [x (count scenes)]
#_=> (map #(play scenes %1) (range x))))
#'user/play-all
user=> (play-all scenes)
("Frankie say relax" "Lucy loves Clojure" "Rich tries a new conditioner")
(I prefer the latter.)
Edit: If you like ->>, this is even better:
user=> (defn play-all [scenes]
#_=> "Play all the scenes"
#_=> (->> scenes
#_=> (count)
#_=> (range)
#_=> (map #(play scenes %))))
#'user/play-all
user=> (play-all scenes)
("Frankie say relax" "Lucy loves Clojure" "Rich tries a new conditioner")
map iterates over a collection (or several collections) to produce a sequence.
for build a sequence from a list comprehension.
In your case you can use either one or the other.
In terms of decomposition, it would make sens to have actually a function that plays one scene :
(defn play-one [scene]
"Play a scene"
(->>
scene
((juxt :subject :action :object))
(interpose " ")
(apply str)))
Then playing the nth can use precedent definition:
(defn play-nth [scenes n]
"Play the n(th) scene"
(->
scenes
(#(get % n))
play-one))
And you have several ways to play all the scenes:
(defn play-all-map1 [scenes]
"Play all the scenes"
(map (partial play-nth scenes) (range (count scenes))))
But you can really simplify since you don't need range because scenes can be treated as a sequence (assuming you are not interested in the index):
(defn play-all-map2 [scenes]
"Play all the scenes with map"
(map play-one scenes))
And with for:
(defn play-all-for [scenes]
"Play all the scenes with for"
(for [scene scenes]
(play-one scene)))
Before we do anything with it, your play function should deal with a single scene:
(defn play [scene]
"Play a scene"
(->> scene
((juxt :subject :action :object))
(interpose " ")
(apply str)))
You use it like so:
(play (scenes 0))
;"Frankie say relax"
... which is no easier or harder than before. But
it will work with any scene and
the scenes don't have to be kept in a vector.
It also makes play-all simpler:
(defn play-all [ss]
(map play ss))
(play-all scenes)
;("Frankie say relax" "Lucy loves Clojure" "Rich tries a new conditioner")
I was tempted to replace ((juxt :subject :action :object)) with vals in play, but we can't rely on the sequence order of the map entries.

dependency injection between app and library in clojure

I am splitting part of my app into a library
The library functionality has certain dependencies which must be injected by the app. I have modeled this with a protocol
(defprotocol MyLibDependencies
(some-injected-capability [x]))
(defrecord Foo [y])
(defrecord Bar [y])
(defmulti render-response class)
(defmethod render-response Foo [val] {:ok (some-injected-capability (:y val))})
(defmethod render-response Bar [val] {:err (some-injected-capability (:y val))})
and here in the application I can provide an implementation:
(extend-type Object
MyLibDependencies
(some-injected-capability [x] (inc x)))
(comment
(render-response (Foo. 10)) ;; => {:ok 11}
(render-response (Bar. 10)) ;; => {:err 11}
)
This works, however it feels like an abuse of protocols, because I need neither polymorphic dispatch, nor does the injected function necessarily require an argument (the protocol requires at least one argument to dispatch against it's class). What are my options?
Note that the records Foo and Bar are library domain types, and the render-response method is also library domain. I don't necessarily care how I define them, but the abstractions they represent are library domain.
This is a bit closer to how you would typically see protocols used to provide functionality from client code to a library:
;; lib.clj
(defprotocol MyLibDependencies
(provide-status [this x])
(provide-response [this x]))
(defn render-response
[responder val]
{:status (provide-status responder val)
:code (provide-response responder val)})
;; client.clj
(defrecord Foo [y]
MyLibDependencies
(provide-status [this val]
(if (even? val)
:ok
:err))
(provide-response [this val]
(+ y val)))
(defrecord Bar [y]
MyLibDependencies
(provide-status [this val]
(if (odd? val)
:ok
:err))
(provide-response [this val]
(+ y val)))
(comment
(render-response (Bar. 10) 1) ;; => {:status :ok :code 11}
(render-response (Foo. 10) 1) ;; => {:status :err :code 11}
)
There are numerous examples of this style of Clojure code in the wild - in fact most of the core functions that make up Clojure itself are end up resolving to calls to protocol methods provided by the specific datastructure being used, or multimethod calls extended for the individual datatypes.

Converting a string into a function that is not in a namespace in clojure

Here is the sample code I want to get to work:
(letfn [(CONC [f] f)
(CONT [f] (str "\newline" f))]
((voodoo "CONC") "hamster"))
Is there some voodo that will make it call the CONC function with hamster as the parameter? That is, is there some way to convert the string "CONC" into a function that is not bound to a namespace but rather to a local binding?
EDIT:
To be clearer, the way this will be called is:
(map #((voodoo (:tag %)) (:value %))
[
{:tag "CONC" :value "hamster"}
{:tag "CONT" :value "gerbil"}
]
)
I'd probably solve this by creating a map of functions indexed by strings:
(def voodoo
{"CONC" (fn [f] f)
"CONT" (fn [f] (str "\newline" f))})
Then your desired code should work directly (exploiting the fact that a map is a function that looks up it's argument)
(map #((voodoo (:tag %)) (:value %))
[
{:tag "CONC" :value "hamster"}
{:tag "CONT" :value "gerbil"}
]
)
Note that the functions here are fully anonymous - you don't need them to be referenced anywhere in the namespace for this to work. In my view this is a good thing, because unless you also need the functions somewhere else then it's best to avoid polluting your top-level namespace too much.
No. Eval does not have access to the local/lexical environment, ever.
Edit: This is not a very good answer, and not really accurate either. You could write voodoo as a macro, and then it doesn't need runtime access to the lexical environment, just compile-time. However, this means it would only work if you know at compile time that the function you want to call is x, and so it wouldn't be very useful - why not just type x instead of (voodoo "x")?
(defmacro voodoo [fname]
(symbol fname))
(letfn [(x [y] (inc y))]
((voodoo "x") 2))
;; 3
(letfn [(x [y] (inc y))]
(let [f "x"]
((voodoo f) 2)))
;; error
Well, it's sort of possible:
(defmacro voodoo [s]
(let [env (zipmap (map (partial list 'quote) (keys &env))
(keys &env))]
`(if-let [v# (~env (symbol ~s))]
v#
(throw (RuntimeException. "no such local")))))
...and now we can do weird stuff like this:
user> (defn example [s]
(letfn [(foo [x] {:foo x})
(bar [x] {:bar x})]
((voodoo s) :quux)))
#'user/example
user> (example "foo")
{:foo :quux}
user> (example "bar")
{:bar :quux}
user> (example "quux")
; Evaluation aborted.
user> *e
#<RuntimeException java.lang.RuntimeException: no such local>
That "Evaluation aborted" means an exception was thrown.
You could also replace the throw branch of the if in voodoo with (resolve (symbol ~s)) to defer to the globals if no local is found:
(defmacro voodoo [s]
(let [env (zipmap (map (partial list 'quote) (keys &env))
(keys &env))]
`(if-let [v# (~env (symbol ~s))]
v#
(resolve (symbol ~s)))))
...and now this works with definition of example as above (though note that if you are experimenting at the REPL, you will need to recompile example after redefining voodoo):
user> (defn quux [x] {:quux x})
#'user/quux
user> (example "quux")
{:quux :quux}
Now, this is an abuse of Clojure's facilities which one would do well to try to do without. If one cannot, one should probably turn to evalive by Michael Fogus; it's a library which provides an "eval-with-locals" facility in the form of an evil function and a couple of utilities. The functionality seems to be well factored too, e.g. something like the ~(zipmap ...) thing above is encapsulated as a macro and evil there appears to be almost a drop-in replacement for eval (add the env parameter and you're good to go). I haven't read the source properly, but I probably will now, looks like fun. :-)
Im not really clear what you are asking for so i'll try a couple answers:
if you have a string that is the name of the function you wish to call:
(def name "+")
((find-var (symbol (str *ns* "/" name))) 1 2 3)
this would give voodoo a deffinition like this:
(defn voodoo [name args] (apply (find-var (symbol (str *ns* "/" name))) args))
#'clojure.core/voodoo
clojure.core=> (voodoo "+" [1 2 3])
6
clojure.core=>
this assumes your function is in the current namepace ns.
if you want to turn a string into a function you could use this pattern
(let [f (eval (read-string "(fn [] 4)"))] (f))