Fork me on GitHub
#clojure
<
2023-03-22
>
nodename02:03:49

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?

hifumi12302:03:02

Assuming you use leiningen, this is exactly what :managed-dependencies is for

hifumi12302:03:56

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

Joseph Graham04:03:50

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?

seancorfield05:03:02

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.

seancorfield05:03:25

Then (update-in data [12 :age] inc) will do what you need.

seancorfield05:03:13

Otherwise, yes, map'ing a conditional update is probably the best you're going to do...

seancorfield05:03:40

(into {} (map (juxt :id identity)) mymap) will give you the map of maps BTW.

👍 4
Joseph Graham05:03:20

right yes I guess it should be obvious to use a map of maps

Joseph Graham05:03:10

need to learn juxt too

skylize06:03:34

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))

Joseph Graham06:03:11

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.

Joseph Graham06:03:50

didn't think to put function definitions in a let before for some reason

jpmonettas11:03:58

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

pppaul17:03:06

just adding this as a walk example

(->> mymap
     (clojure.walk/postwalk
       (fn [{:keys [id] :as form}]
         (cond-> form
           (= id 12) (update :age inc)))))

kanwei15:03:52

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}

p-himik15:03:05

You can create a custom reader tag that would resolve a symbol.

dpsutton15:03:20

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

2
kanwei15:03:33

Thanks! I changed it to

:daemons/test                   {:heartbeat "xxx"
                                  :fn 'webapp.backend/email-daemon}

kanwei15:03:50

however

(resolve (:fn (env :daemons/test)))

kanwei15:03:51

returns nil

kanwei15:03:17

but

(resolve 'webapp.backend/email-daemon)

kanwei15:03:17

works in the repl

p-himik15:03:58

Try requiring-resolve.

kanwei15:03:25

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.

kanwei15:03:34

even though i'm running in the actual file

kanwei15:03:27

do I need to pass in an env to (resolve)?

pesterhazy15:03:32

So you need the apostrophe in the edn?

dpsutton15:03:44

yeah. don’t quote your edn

2
dpsutton15:03:46

table-test=> (resolve (:a (clojure.edn/read-string "{:a inc}")))
#'clojure.core/inc
table-test=> (resolve (:a (clojure.edn/read-string "{:a 'inc}")))
nil

kanwei15:03:54

ok that worked, I didn't realize the literal was a symbol already in the edn

🎯 2
kanwei15:03:06

thanks yall 🙂

Mark Wardle19:03:09

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.