Is there any way to put a function into an aero config.edn? I want something like this:
:daemons/test {:heartbeat "xxx"
:fn #'webapp.backend/email-daemon}You can create a custom reader tag that would resolve a symbol.
edn is a text format. Can’t hold references to functions. Just put the symbol there and then map it to the var wherever you use the config
Thanks! I changed it to
:daemons/test {:heartbeat "xxx"
:fn 'webapp.backend/email-daemon}returns nil
works in the repl
Try requiring-resolve.
Execution error (FileNotFoundException) at webapp.backend/eval84119 (backend.clj:120).
Could not locate 'webapp/backend__init.class, 'webapp/backend.clj or 'webapp/backend.cljc on classpath.even though i'm running in the actual file
do I need to pass in an env to (resolve)?
So you need the apostrophe in the edn?
table-test=> (resolve (:a (clojure.edn/read-string "{:a inc}")))
#'clojure.core/inc
table-test=> (resolve (:a (clojure.edn/read-string "{:a 'inc}")))
nilok that worked, I didn't realize the literal was a symbol already in the edn
thanks yall 🙂
Other approach to the problem: I added a reader called #clj/var - eg https://github.com/wardle/pc4/blob/main/pc4-server/resources/config.edn and https://github.com/wardle/pc4/blob/main/pc4-server/src/clj/com/eldrix/pc4/system.clj so that the var is resolved automatically by those ingesting the config data and they don’t need to be concerned re how resolved.
Question about “re-exporting” namespaces: I have front-end project (cljs), back-end project (clj), shared project A (cljc), and shared project B (cljc). Front-end project depends on shared project A and shared project B; shared project A also depends on shared project B, and they must both use the same version of B. I am thinking that if all access to B was through A, then the manual version sync wd not be needed. Is there a way to accomplish this while still keeping B as a separate project?
Thank you @hifumi123!
Assuming you use leiningen, this is exactly what :managed-dependencies is for
I have a monolithic project similarly structured to this and I use managed dependencies to ensure if several projects depend on X then they all use the exact same version specified in a root project.clj file
Given a list of maps like this:
(def mymap [{:id 12 :name "Bob" :age 25} {:id 13 :name "Alice" :age 23}])
To increment age where :id is 12. The only way I could think to do it is to use map like this:
(map #(if (= (% :id) 12) (update % :age inc) %) mymap)
Is it idiomatic?
you can also change
#(if (= (% :id) 12) (update % :age inc) %) into
#(cond-> % (= (% :id) 12) (update :age inc))
which I think is pretty idiomatic to modify something conditionally and a little easier to extend if you add more modifications under more conditions
just adding this as a walk example
(->> mymap
(clojure.walk/postwalk
(fn [{:keys [id] :as form}]
(cond-> form
(= id 12) (update :age inc)))))It depends. If you're more likely to be accessing those maps by :id than by index, use a map of maps instead of a vector of maps.
Then (update-in data [12 :age] inc) will do what you need.
Otherwise, yes, map'ing a conditional update is probably the best you're going to do...
(into {} (map (juxt :id identity)) mymap) will give you the map of maps BTW.
right yes I guess it should be obvious to use a map of maps
thankyou!
need to learn juxt too
Assuming that you did need to keep this as a sequence for whatever reason, I think just pulling the transform function out from the map call helps a lot with the ugliness and readability problems of your example.
(let [transform
#(if (= (% :id) 12)
(update % :age inc)
%)
(map transform mymap)
You could also take this further. Pull the generic control flow out into a function, maybe even raising that to be a top level function. Then come up with locally-meaningful descriptive names for all the little bits of logic. The end result is a composition of the named pieces.
(defn modify-if [pred f x]
(if (pred x) (f x) x))
(let [id=12 #(= 12 (:id %))
inc-age #(update % :age inc)
inc-except-12 #(modify-if id=12 inc-age %)]
(map inc-except-12 mymap))yes that's definitely nicer. I'll do that for now as I'll have to do some substantial refactoring to make it use a map of maps.
didn't think to put function definitions in a let before for some reason