Consider the following functions in an MVE (minimal viable example) namespace from a fresh lein new app arrow-mve. The function extract-one is public, and the function extract-two is private. I've included the main- function just for completeness and for the remote possibility that it's entailed in my problem:
(ns arrow-mve.core
(:gen-class))
(defn extract-one [m]
(-> m :a))
(defn- extract-two [m]
(-> m :a))
(defn -main
"I don't do a whole lot ... yet."
[& args]
(println "Hello, World!"))
In my parallel test namespace, I can test these functions as follows. I can test the public function extract-one either by a direct call or using the arrow-threading macro ->. Also notice that I have no problem referring to the private function, extract-two, by its full Var in a direct call. These tests pass:
(ns arrow-mve.core-test
(:require [clojure.test :refer :all]
[arrow-mve.core :refer :all]))
(deftest test-one-a
(is (= 1 (extract-one {:a 1, :b 2}))))
(deftest test-one-b
(is (= 1 (-> {:a 1, :b 2}
extract-one))))
(deftest test-two-a
(is (= 1 (#'arrow-mve.core/extract-two
{:a 1, :b 2}))))
But I get a compile error when I attempt to call the private function extract-two with the arrow macro:
(deftest test-two-b
(is (= 1 (-> {:a 1, :b 2}
#'arrow-mve.core/extract-two))))
$ lein test
Exception in thread "main" java.lang.RuntimeException: Unable to resolve
var: arrow.mve.core/extract-two in this context, compiling:
(arrow_mve/core_test.clj:10:12)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6875)
at clojure.lang.Compiler.analyze(Compiler.java:6669)
at clojure.lang.Compiler.analyze(Compiler.java:6625)
Things get more strange when I make the test a little more complex.
(deftest test-two-b
(is (= {:x 3.14, :y 2.72}
(-> {:a {:x 3.14, :y 2.72}, :b 2}
#'arrow-mve.core/extract-two))))
$ lein test
Exception in thread "main" java.lang.ClassCastException:
clojure.lang.PersistentArrayMap cannot be cast to clojure.lang.Symbol,
compiling:(arrow_mve/core_test.clj:18:10)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6875)
at clojure.lang.Compiler.analyze(Compiler.java:6669)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6856)
Again, the test passes in the direct-call form:
(deftest test-two-b
(is (= {:x 3.14, :y 2.72}
(#'arrow-mve.core/extract-two
{:a {:x 3.14, :y 2.72}, :b 2}))))
I suspect that the problem is a limitation of macro-chaining through deftest, is, the reader macro #' for Var, and the arrow macro, and wondered if it was by design or a potential bug. Of course, in my real application (not this MVE), I have long and deep call chains that make using the arrow macros highly desirable.
Here is the answer (different ns):
Main namespace:
(ns clj.core
(:require [tupelo.core :as t] ))
(t/refer-tupelo)
(defn extract-one [m]
(-> m :a))
(defn- extract-two [m]
(-> m :a))
Testing namespace:
(ns tst.clj.core
(:use clj.core
clojure.test )
(:require [tupelo.core :as t]))
(t/refer-tupelo)
(deftest test-one-a
(is (= 1 (extract-one {:a 1, :b 2}))))
(deftest test-one-b
(is (= 1 (-> {:a 1, :b 2}
extract-one))))
(deftest test-two-a1
(is (= 1 (#'clj.core/extract-two {:a 1, :b 2}))))
;(deftest test-two-b
; (is (= 1 (-> {:a 1, :b 2}
; clj.core/extract-two)))) ; fails: not public
;(deftest test-two-b1
; (is (= 1 (-> {:a 1, :b 2}
; #'clj.core/extract-two))))
; fails: can't cast PersistentArrayMap to Symbol
(deftest test-two-b
(is (= 1 (-> {:a 1, :b 2}
(#'clj.core/extract-two))))) ; works
The answer is that the var reference needs to be inside parentheses. The thread macros all have a test of the form (pseudocode):
(if (not (list? form))
'(form)
form)
So a form like
(-> 1
inc)
is transformed into
(-> 1
(inc))
before the rest of the threading occurs. The if test seems to be failing for you since the var is not a symbol. Enclosing the var in a list as a function call fixes the problem.
I prefer to always enclose the function calls in threading forms in parentheses, and not use any "naked" functions even though it is normally allowable:
(-> 1
(inc) ; could have typed "inc" w/o parens
(* 2)) ; must use parens since more than 1 arg
;=> 4
Related
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.
Is it possible to both destructure and rename keys in one go?
Consider this:
(let [{:keys [response]} {:response 1}]
(println response))
However, if I want to instead refer to 1 as my-response, I have to do something like:
(let [{:keys [my-response]} (clojure.set/rename-keys {:response 1} {:response :my-response})]
(println my-response))
Obviously this does not work with defn destructuring.
Is there any way in Clojure to both destructure and rename keys?
Use destructuring without :keys:
(let [{my-response :response} {:response 1}]
(println my-response))
{:keys [response]} is syntactic sugar for {response :response}.
Here you go:
(let [{:keys [response]} {:response 1}
my-response response]
(println my-response))
For a better answer refer to https://stackoverflow.com/a/57592661/2757027.
This answer is much closer to the question, but technically not single step. but this doesn't involve any complicated de-structuring.
If you don't mind using a library, a more powerful destructuring tool is available from tupelo.core/destruct. Here is an example:
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test))
(dotest
(let [info {:a 777
:b [2 3 4]}
mania [{:a 11} {:b 22} {:c [7 8 9]}]]
(let [z ::dummy]
(destruct [info {:a z
:b [d e f]}
mania [{:a ?} BBB {:c clutter}]]
(is= z 777)
(is= [d e f] [2 3 4])
(is= a 11)
(is= BBB {:b 22})
(is= clutter [7 8 9])))))
So you can see that inside the destruct expression, the symbols z, d, e, f, BBB, and clutter are given the corresponding values from the input variables info and mania. The special symbol ? is interpreted to mean that the keyword :a creates a symbol a to receive the value 11.
I am working through the beginning of a Clojure for the Brave and True example:
(ns the-divine-cheese-code.visualization.svg
(:require [clojure.string :as s])
(:refer-clojure :exclude [min max]))
(defn comparator-over-maps
[comparison-fn ks]
(fn [maps]
(zipmap ks
(map (fn [k] (apply comparison-fn (map k maps)))
ks))))
(def min (comparator-over-maps clojure.core/min [:lat :lng]))
(def max (comparator-over-maps clojure.core/max [:lat :lng]))
I am getting a Null Pointer Exception, though, when I try to run the following code in a CIDER REPL:
(min [{:a 1 :b 3} {:a 5 :b 0}])
I am trying to identify the source of the error within the code. Any help would certainly be appreciated.
The function comparator-over-maps uses the vector of keywords that you pass it to look up values in the map. In this case the maps you're passing have keys :a and :b, but your definition of min is requesting the keys :lat and :lng, which don't exist -- so it receives nil, which is the cause of the NPE. If you change one or the other set of keywords to match, then the example should work, e.g.:
(min [{:lat 1 :lng 3} {:lat 5 :lng 0}])
Based on #BlackBear's comment, I reran the code in the CIDER REPL as:
(min [{:lat 1 :lng 3} {:lat 5 :lng 0}])
and it produced the correct answer:
=> {:lat 1, :lng 0}
Thanks for your help!
I'm trying to write a macro that can be used both in a global and nested way, like so:
;;; global:
(do-stuff 1)
;;; nested, within a "with-context" block:
(with-context {:foo :bar}
(do-stuff 2)
(do-stuff 3))
When used in the nested way, do-stuff should have access to {:foo :bar} set by with-context.
I've been able to implement it like this:
(def ^:dynamic *ctx* nil)
(defmacro with-context [ctx & body]
`(binding [*ctx* ~ctx]
(do ~#body)))
(defmacro do-stuff [v]
`(if *ctx*
(println "within context" *ctx* ":" ~v)
(println "no context:" ~v)))
However, I've been trying to shift the if within do-stuff from runtime to compile-time, because whether do-stuff is being called from within the body of with-context or globally is an information that's already available at compile-time.
Unfortunately, I've not been able to find a solution, because nested macros seem to get expanded in multiple "macro expansion runs", so the dynamic binding of *ctx* (as set within with-context) is not available anymore when do-stuff gets expanded. So this does not work:
(def ^:dynamic *ctx* nil)
(defmacro with-context [ctx & body]
(binding [*ctx* ctx]
`(do ~#body)))
(defmacro do-stuff [v]
(if *ctx*
`(println "within context" ~*ctx* ":" ~v)
`(println "no context:" ~v)))
Any ideas how to accomplish this?
Or is my approach totally insane and there's a pattern for how to pass state in such a way from one macro to a nested one?
EDIT:
The body of with-context should be able to work with arbitrary expressions, not only with do-stuff (or other context aware functions/macros). So something like this should also be possible:
(with-context {:foo :bar}
(do-stuff 2)
(some-arbitrary-function)
(do-stuff 3))
(I'm aware that some-arbitrary-function is about side effects, it might write something to a database for example.)
When the code is being macroexpanded, Clojure computes a fixpoint:
(defn macroexpand
"Repeatedly calls macroexpand-1 on form until it no longer
represents a macro form, then returns it. Note neither
macroexpand-1 nor macroexpand expand macros in subforms."
{:added "1.0"
:static true}
[form]
(let [ex (macroexpand-1 form)]
(if (identical? ex form)
form
(macroexpand ex))))
Any binding you establish during the execution of a macro is no more in place when you exit your macro (this happens inside macroexpand-1). By the time an inner macro is being expanded, the context is long gone.
But, you can call macroexpand directly, in which case the binding are still effective. Note however that in your case, you probably need to call macroexpand-all.
This answer explains the differences between macroexpand and clojure.walk/macroexpand-all: basically, you need to make sure all inner forms are macroexanded.
The source code for macroexpand-all shows how it is implemented.
So, you can implement your macro as follows:
(defmacro with-context [ctx form]
(binding [*ctx* ctx]
(clojure.walk/macroexpand-all form)))
In that case, the dynamic bindings should be visible from inside the inner macros.
I'd keep it simple.
This is solution avoids state in an additional *ctx* variable. I think it is a more functional approach.
(defmacro do-stuff
([arg1 context]
`(do (prn :arg1 ~arg1 :context ~context))
{:a 4 :b 5})
([arg1]
`(prn :arg1 ~arg1 :no-context)))
(->> {:a 3 :b 4}
(do-stuff 1)
(do-stuff 2))
output:
:arg1 1 :context {:a 3, :b 4}
:arg1 2 :context {:b 5, :a 4}
there is one more variant to do this, using some macro magic:
(defmacro with-context [ctx & body]
(let [ctx (eval ctx)]
`(let [~'&ctx ~ctx]
(binding [*ctx* ~ctx]
(do ~#body)))))
in this definition we introduce another let binding for ctx. Clojure's macro system would then put it into the &env variable, accessible by the inner macros at compile-time. Notice that we also keep bindings so that inner functions could use it.
now we need to define the function to get the context value from macro's &env:
(defn env-ctx [env]
(some-> env ('&ctx) .init .eval))
and then you can easily define do-stuff:
(defmacro do-stuff [v]
(if-let [ctx (env-ctx &env)]
`(println "within context" ~ctx ":" ~v)
`(println "no context:" ~v)))
in repl:
user> (defn my-fun []
(println "context in fn is: " *ctx*))
#'user/my-fun
user> (defmacro my-macro []
`(do-stuff 100))
#'user/my-macro
user> (with-context {:a 10 :b 20}
(do-stuff 1)
(my-fun)
(my-macro)
(do-stuff 2))
;;within context {:a 10, :b 20} : 1
;;context in fn is: {:a 10, :b 20}
;;within context {:a 10, :b 20} : 100
;;within context {:a 10, :b 20} : 2
nil
user> (do (do-stuff 1)
(my-fun)
(my-macro)
(do-stuff 2))
;;no context: 1
;;context in fn is: nil
;;no context: 100
;;no context: 2
nil
I'm trying to coerce a map using prismatic-schema (1.0.4)
I'm trying to coerce
{:a 1}
to
{:b 1}
Using a custom matcher with the schema:
{:b s/Int}
But this code isn't working:
(require '[schema.core :as s])
(require '[schema.coerce :as coerce])
((coerce/coercer {:b s/Int}
(fn [s]
(when (= s s/Keyword)
(fn [x]
(if (= x :a)
:b
x)))))
{:a 1})
Output:
#schema.utils.ErrorContainer{:error {:b missing-required-key, :a disallowed-key}}
I tried debugging it by running the following code which matches everything in the schema and outputs the current value and schema being matched:
((coerce/coercer {:b s/Int}
(fn [s]
(when true
(fn [x]
(println s x)
x))))
{:a 1})
Output:
{:b Int} {:a 1}
=>
#schema.utils.ErrorContainer{:error {:b missing-required-key, :a disallowed-key}}
It looks as though the matcher bombs out as soon as it gets to the map?
Schema first breaks your map up into pieces that match up to the schema, then coerces each MapEntry to the corresponding MapEntry schema, and so on down. This breakdown fails in your case, so you never get to the key.
To accomplish what you want, you'll have to attach the coercion to the map schema and use e.g. clojure.set/rename-keys in your coercion function:
(def Foo {:b s/Int})
((coerce/coercer
Foo
(fn [s]
(when (= s Foo)
#(clojure.set/rename-keys % {:a :b}))))
{:a 1})