I am registering a symbol dynamically and I can verify it is successful:
(defn- register[x]
(intern 'foo.core x (atom {}))
(println "Var " x " gets " (var-get (get (ns-interns 'foo.core) x))))
; Var Persons gets #object[clojure.lang.Atom 0x688a2c09 {:status :ready, :val {}}]
My core question is: how to use x in (swap!...
Second question is: how to dereference x to get the map itself?
Final question is: Is there a cleaner approach?
intern function returns a variable object, and since it is a reference value it can be dereferenced (with # or deref). It can also be returned from the function and operated on, to get the desired result. The value this var points to (or the var's binding) points to an atom, and that is another reference value, and can also be dereferenced.
here's a little example of how to deal with this case:
user> (ns my.ns)
nil
my.ns> (in-ns 'user)
#namespace[user]
user> (defn register-trace [v-name]
(let [v (intern 'my.ns v-name (atom {}))]
(println (str "v is " v))
(println (str "v's contents is " #v))
(println (str "v's internal value is " ##v))
v))
#'user/register-trace
user> (register-trace 'asd)
;;=> v is #'my.ns/asd
;;=> v's contents is clojure.lang.Atom#6a6ee04b
;;=> v's internal value is {}
#'my.ns/asd
user> (let [var-atom #(register-trace 'my-var)]
(swap! var-atom assoc :x 101))
;;=> v is #'my.ns/my-var
;;=> v's contents is clojure.lang.Atom#2164d9ed
;;=> v's internal value is {}
{:x 101}
user> #my.ns/my-var
{:x 101}
user> (swap! my.ns/my-var update :x inc)
{:x 102}
user> #my.ns/my-var
{:x 102}
Related
This question already has answers here:
In Clojure, how to destructure all the keys of a map?
(3 answers)
Closed 4 years ago.
I have this sample code where I create vars by iterating over the key value pair of the map.
(defmacro block [bindings & body]
`(let [
~#(mapcat (fn [[k v]] [(if (symbol? k) k (symbol (name k))) `~v]) bindings) ]
~body))
(block {:a 1 :b 2} (if true (prn "true" a b) (prn "false")))
It works fine.
Ouput: "true" 1 2
But now I want to pass the same map as a var but, it thows an exception.
IllegalArgumentException Don't know how to create ISeq from: clojure.lang.Symbol
(def ctx {:a 3 :b 4})
(block ctx (if true (prn "true" a b) (prn "false")))
The reason it doesn't work, when you pass a symbol that refers to a var that holds a map, is because the macro is only seeing the symbol literal — not the value it refers to. When you pass it a map literal, the macro is seeing the map literal.
If you wanted it to also support symbols, you'll need to resolve the var the symbol refers to and get its value e.g. (var-get (resolve bindings)):
(defmacro block [bindings & body]
`(let [~#(mapcat (fn [[k v]] [(if (symbol? k)
k
(symbol (name k))) `~v])
(cond
(map? bindings) bindings
(symbol? bindings) (var-get (resolve bindings))
:else (throw (Exception. "bindings must be map or symbol"))))]
~#body))
(block ctx (if true (prn "true" a b) (prn "false")))
"true" 3 4
(block {:a 3 :b 4} (if true (prn "true" a b) (prn "false")))
"true" 3 4
You also need to "splice" body into the form using ~#, because body will be a sequence of forms even if it only contains one form.
It can also help to look at how your macro expands when troubleshooting it:
(macroexpand-1 '(block ctx (if true (prn "true" a b) (prn "false"))))
=> (clojure.core/let [a 3 b 4] (if true (prn "true" a b) (prn "false")))
I have an application with a lot of large maps and other things, which are clumsy to read when printed, so I made a custom print function for them and set up print-method to call it, like this:
(defmethod print-method clojure.lang.PersistentArrayMap [v ^java.io.Writer w]
(.write w (fstr1 v)))
Inside fstr1, how can I call the ordinary print-method if I determine that the map is not one of the kinds that require special treatment?
This answer suggests putting a :type in the metadata, since print-method dispatches on that. I've had some success with that, but I can't always control the metadata, so I'm hoping there's a way to "forward" to the previously defined print-method from within fstr1.
For reference, here's my current implementation of fstr1:
(defn fstr1 ^String [x]
(cond
(ubergraph? x)
(fstr-ubergraph x)
(map? x)
(case (:type x)
:move (fstr-move x)
:workspace "(a workspace)"
:bdx (fstr-bdx x)
:rxn (fstr-rxn x)
(apply str (strip-type x)))
:else
(apply str (strip-type x))))
You can always rebind print-object and tuck the real print-object away so you can call it when appropriate:
user> (let [real-print-method print-method]
(with-redefs [print-method (fn [v w]
(if (and (map? v)
(:foo v))
(do
(real-print-method "{:foo " w)
(real-print-method (:foo v) w)
(real-print-method " ...}" w))
(real-print-method v w)))]
(println {:foo 42 :bar 23} {:baz 11 :quux 0})))
{:foo 42 ...} {:baz 11, :quux 0}
nil
user>
Is there a short form/macro that allows me to do
(defn f [a b c]
{a b c})
instead of
(defn f [a b c]
{:a a :b b :c c})
(defmacro as-map [& syms]
(zipmap (map keyword syms) syms))
Usage:
(def a 42)
(def b :foo)
(as-map a b)
;;-> {:a 42 :b :foo}
Note that to support namespaced keywords, you'd have to drop support for ns aliases if you want to keep it as short:
(defmacro as-map [& syms]
(zipmap (map keyword syms) (map (comp symbol name) syms)))
Usage:
(def a 42)
(def b :foo)
(as-map example/a foo-of/b)
;;-> {:example/a 42 :foo-of/b :foo}
Advice: Likely not a good idea, saves you a few keyboard hits at the cost of readability and expressivity and flexibility in naming local bindings.
This shows the steps. Remove the println's for actual use:
(ns clj.core
(:gen-class))
(defmacro hasher [& args]
(let [keywords (map keyword args)
values args
keyvals-list (interleave keywords values)
]
(println "keywords " keywords)
(println "values " values)
(println "keyvals-list " keyvals-list)
`(hash-map ~#keyvals-list)
)
)
(def a 1)
(def b 2)
(println \newline "result: " (hasher a b))
> lein run
keywords (:a :b)
values (a b)
keyvals-list (:a a :b b)
result: {:b 2, :a 1}
This is an old snippet of mine I've had kicking around for a while.
(declare ^:private restructure*)
(defn ^:private restructure-1 [m [e k]]
(cond
(= :strs e) (reduce #(assoc %1 (name %2) %2) m k)
(= :keys e) (reduce #(assoc %1 (keyword (namespace %2) (name %2)) %2) m k)
:else (assoc m k (restructure* e))))
(defn ^:private restructure* [form]
(if-not (map? form)
form
(as-> {} v
(reduce restructure-1 v form)
`(hash-map ~#(mapcat identity v)))))
(defmacro restructure [form]
(restructure* form))
The idea is that it provides the complement of clojure.core/destructure which goes from a destructuring form to bindings, this captures bindings and constructs a datastructure.
(let [x 1 y 2 z 3]
(restructure {:keys [x y z]}))
;; => {:x 1 :y 2 :z 3}
Is there a convenient way in ClojureScript to pretty print a nested hash-map in the way that the whole tree-structure becomes immediately visible.
For instance a map like this
(def my-map {:a {:b 1 :c 9} :b {:d 8 :e {:f 2 :g 3 :h 4}} :c 10})
should be printed like this:
{:a {:b 1
:c 9}
:b {:d 8
:e {:f 2
:g 3
:h 4}}
:c 10}
EDIT: There might also be vectors in the map. The usecase is just to inspect larger data structures during development.
There is no built-in way to do it. You might come close to what you want by using cljs.pprint and setting cljs.pprint/*print-right-margin* to a low value.
I would recommend to take a look at a small library shodan which provides a very useful inspect function:
(require '[shodan.inspection :refer [inspect]])
(inspect {:aaaaaa 1
:bbbbbb {:ccc 2
:dddddd [1 2 3 4 5]}})
It won't print anything in your CLJS REPL but will provide a handy view in your browser's console:
You can collapse and expand nested datastructures - it basically does what you asked for.
As a personal challenge I wrote the following code:
(enable-console-print!)
(def atomic? (complement coll?))
(def padding #(apply str (repeat % " ")))
(def tabulate #(apply str (repeat % "\t")))
(def strcat #(->> (apply concat %&) (apply str)))
(defn my-max-key [x] (if (empty? x) [""] (apply (partial max-key count) x)))
(defn longest-key [m] (->> m keys (filter atomic?) (map str) my-max-key))
(def length (comp count str))
(def not-map? (complement map?))
(def nested? #(some coll? %))
(def join #(apply str (interpose % %2)))
(def join-lines (partial join "\n"))
(defn has-atomic? [coll] (some atomic? coll))
(defn diff-key-lengths [key1 key2] (- (length key1) (length key2)))
(defn convert
([thing] (convert -1 thing))
([depth thing]
(defn convert-items []
(defn convert-seq []
(conj []
(map (partial convert (inc depth)) thing)
""))
(defn string-horizontally [[key value]]
(str (tabulate (inc depth))
key
(padding (diff-key-lengths (longest-key thing) key))
" → "
value))
(defn string-vertically [[key value]]
(str (convert (inc depth) key) "\n"
(convert (+ 2 depth) "↓") "\n"
(convert (inc depth) value) "\n"))
(defn convert-kv [[key value]]
(if (nested? [key value])
(string-vertically [key value])
(string-horizontally [key value])))
(cond (atomic? thing)
[(str (tabulate depth) thing)]
(not-map? thing)
(convert-seq)
(map? thing)
(map convert-kv thing)))
(->> (convert-items) flatten join-lines)))
(def sample-input [["the first thing in this nested vector"]
{{"this is a key in a nested map"
"that points to me!!!"}
{"and that entire map points to this map!!!"
"cool!!!"
"but it gets cooler cause..."
"the value's line up!!!"}}])
(->> sample-input convert println)
The terminal output is (psst... the values in a map do line up but I don't think that chrome uses a monospaced font!):
Is there an easy way in Clojure (maybe using specter) to filter collections depending on whether the an arbitrarily nested key with a known name contains an element ?
Ex. :
(def coll [{:res [{:a [{:thekey [
"the value I am looking for"
...
]
}
]}
{:res ...}
{:res ...}
]}])
Knowing that :a could have a different name, and that :thekey could be nested somewhere else.
Let's say I would like to do :
#(find-nested :thekey #{"the value I am looking for"} coll) ;; returns a vector containing the first element in coll (and maybe others)
use zippers.
in repl:
user> coll
[{:res [{:a [{:thekey ["the value I am looking for"]}]} {:res 1} {:res 1}]}]
user> (require '[clojure.zip :as z])
nil
user> (def cc (z/zipper coll? seq nil coll))
#'user/cc
user> (loop [x cc]
(if (= (z/node x) :thekey)
(z/node (z/next x))
(recur (z/next x))))
["the value I am looking for"]
update:
this version is flawed, since it doesn't care about :thekey being the key in a map, or just keyword in a vector, so it would give unneeded result for coll [[:thekey [1 2 3]]]. Here is an updated version:
(defn lookup-key [k coll]
(let [coll-zip (z/zipper coll? #(if (map? %) (vals %) %) nil coll)]
(loop [x coll-zip]
(when-not (z/end? x)
(if-let [v (-> x z/node k)] v (recur (z/next x)))))))
in repl:
user> (lookup-key :thekey coll)
["the value I am looking for"]
user> (lookup-key :absent coll)
nil
lets say we have the same keyword somewhere in a vector in a coll:
(def coll [{:res [:thekey
{:a [{:thekey ["the value I am looking for"]}]}
{:res 1} {:res 1}]}])
#'user/coll
user> (lookup-key :thekey coll)
["the value I am looking for"]
which is what we need.