beginners

Getting started with Clojure/ClojureScript? Welcome! Also try: https://ask.clojure.org. Check out resources at https://gist.github.com/yogthos/be323be0361c589570a6da4ccc85f58f.
Nassin 2020-10-02T03:45:37.071300Z

How does an alias passed to -M look like? is its value a vector of strings? (in deps.edn)

seancorfield 2020-10-02T04:00:59.073800Z

@kaxaw75836 -M:foo:bar:quux will look for the three separate aliases :foo, :bar, and :quux in the combined deps.edn data (system + user + project), and then the contents of those three aliases will be merged (according to the rules explained in the Clojure CLI docs on http://clojure.org -- some are merged as hash maps, some are concatenated as vectors, some are "last one wins").

seancorfield 2020-10-02T04:01:35.074500Z

I'm not sure where "vector of strings" comes in? The aliases are all read and merged before your program is run.

seancorfield 2020-10-02T04:04:56.076200Z

With the latest CLI, programs started via -M or -X can read the system property that provides the location of an EDN file that you can read to get a full hash map that contains the combined alias data, the lib path data, etc -- the "basis".

Nassin 2020-10-02T04:05:12.076400Z

:aliases {:dev         {:extra-paths ["dev"]}
           :nrepl       {:extra-deps {cider/cider-nrepl {:mvn/version "0.25.3"}}}
           :nrepl-opts  ["-m" "nrepl.cmdline" "--middleware" "[cider.nrepl/cider-middleware]"]}

Nassin 2020-10-02T04:05:52.076700Z

I'm doing clojure -A:dev:nrepl -M:nrepl-opts

Nassin 2020-10-02T04:06:04.076900Z

something wrong there?

seancorfield 2020-10-02T04:09:19.077200Z

You're missing :main-opts

seancorfield 2020-10-02T04:09:35.077700Z

:nrepl-opts {:main-opts ["-m" ...]}

Nassin 2020-10-02T04:10:40.078100Z

yeah, that works, was trying to avoid the WARNING: Use of :main-opts with -A is deprecated. Use -M instead.

seancorfield 2020-10-02T04:11:02.078500Z

clojure -M:dev:nrepl:nrepl-opts will avoid that warning.

seancorfield 2020-10-02T04:14:56.079400Z

Did that work @kaxaw75836?

Nassin 2020-10-02T04:16:03.080500Z

yep, that work thanks, was not seeing the warning again when reverting to -A, but removing .cpcache put it back on

seancorfield 2020-10-02T04:16:55.081200Z

Ah, yes, -Sforce is your friend there

seancorfield 2020-10-02T04:18:36.083600Z

There's definitely going to be a bit of adjustment in the CLI world as all the projects and tutorials get updated to the latest set of options. I've got to go through my open source projects and update the READMEs -- and update clj-new so it generates all the up-to-date deps.edn and project docs etc.

Nassin 2020-10-02T04:18:43.083700Z

I can follow along now, took a while to figure -M -m in the cli

seancorfield 2020-10-02T04:19:27.084600Z

Yeah, there's quite a bit of discussion about the -M -m "repetition" but it's the start of a grand plan that will bring more functionality over the next few releases...

seancorfield 2020-10-02T04:20:40.085900Z

The general guidance is that you use just one of -A (for REPL dependencies), -M (for running any main opts), or -X (for executing functions) and just specify all the aliases you need via that one option.

seancorfield 2020-10-02T04:21:02.086400Z

The confusing part right now is that -A still executes :main-opts -- which it will stop doing at some point.

seancorfield 2020-10-02T04:22:26.088Z

(which is why we still have -R and -C -- but the real solution is to split :main-opts out of the aliases that you would otherwise want to use with -A -- as you have done above -- so you can do clj -A:dev:nrepl to start a REPL but clj -M:dev:nrepl:nrepl-opts to run the nREPL command-line)

👍 1
Nassin 2020-10-02T04:25:40.089400Z

you mean clj -A:dev:nrepl if you want to start a REPL programmatically for example correct? and clj -M:dev:nrepl:nrepl-opts to start it from CLI?

dpsutton 2020-10-02T04:26:06.089600Z

: not /

Nassin 2020-10-02T04:26:23.089900Z

yeah, typo 😉

👍 1
practicalli-john 2020-10-02T14:11:28.096200Z

@kaxaw75836 I've simplified my aliases to just use -M (or -X where supported). Lots of examples here https://github.com/practicalli/clojure-deps-edn

vlaaad 2020-10-02T14:20:53.096400Z

hey, small correction: reveal supports -X flag

👍 1
dharrigan 2020-10-02T14:21:36.096700Z

Say you have this (def foo [{:a "b" :e "f"} {:a "c" :g "h"}])

vlaaad 2020-10-02T14:21:55.097Z

e.g.

{:extra-deps {vlaaad/reveal {:mvn/version "1.0.130"}}
 :ns-default vlaaad.reveal
 :exec-fn repl}

dharrigan 2020-10-02T14:21:57.097200Z

Is there a beter way to remove all a's, than this?

dharrigan 2020-10-02T14:22:00.097400Z

(mapv #(dissoc % :a) foo)
[{:e "f"} {:g "h"}]

dpsutton 2020-10-02T14:23:24.098300Z

that seems to line up semantically with exactly what you are trying to do, ie, remove the :a key from vector of maps. Is there something deficient about it? Wondering why you would ask

dharrigan 2020-10-02T14:24:17.099300Z

Perhaps there was some deep knowledge within the standard library that would do that 🙂, you know, one function and all 🙂

dharrigan 2020-10-02T14:24:35.099800Z

but I'm happy that my first attempt hit home 😉

dharrigan 2020-10-02T14:25:13.100300Z

Always looking to see if I can improve my understanding of the standard library, by learning about a function that I don't know of yet.

vlaaad 2020-10-02T14:30:33.100500Z

then you can use it as is:

$ clj -X:reveal
Clojure 1.10.1
user=> 1
1

vlaaad 2020-10-02T14:31:02.100700Z

override args:

$ clj -X:reveal :title '"hello"'
Clojure 1.10.1
user=> 1
1

vlaaad 2020-10-02T14:31:24.100900Z

use different type of repl:

$ clj -X:reveal io-prepl :title '"hello"'
1
{:tag :ret, :val "1", :ns "user", :ms 0, :form "1"}

vlaaad 2020-10-02T14:31:30.101100Z

it's all already there

dharrigan 2020-10-02T16:24:33.104600Z

I have an API that makes some decisions based upon the input. Depending on a value of one of the input, a requires-resolve is invoked to load in a namespace. Are there any downsides (memory?) of that requires-resolve being invoked again and again after the first time (i.e., once the namespace has been resolved)?

alexmiller 2020-10-02T16:55:53.110800Z

not really. some perf impact but I assume it's negligible unless you're calling it in a hot loop

dharrigan 2020-10-02T16:56:14.111Z

great, thank you. no. it's called infrequently 🙂

practicalli-john 2020-10-02T17:14:23.112300Z

Awesome. I've added the Clojure exec config to the existing aliases in the repository. So the aliases can be used with either -M or -X, with example of using -X to call other REPLS.

👍 1
Michael W 2020-10-02T18:07:14.119300Z

(def colla [{:email "<mailto:A@b.com|A@b.com>" :ip "10.0.0.1"} {:email "<mailto:b@b.com|b@b.com>" :ip "10.0.0.2"}])
(def collb [{:email "<mailto:a@b.com|a@b.com>" :new_email "<mailto:c@b.com|c@b.com>"} {:email "<mailto:B@b.com|B@b.com>" :new_email "<mailto:d@b.com|d@b.com>"}])
How to merge 2 lists of maps where they may share the same field? I have 2 vectors of maps, they may share an :email key, I would like to merge the maps. The only solution I have come up with is really ugly, is there a better way?
(mapv (fn [i]
        (let [email (string/lower-case (:email i))]
          (merge i (first (filter #(= (string/lower-case (:email %)) email) collb))))) colla)
[{:email "<mailto:a@b.com|a@b.com>", :ip "10.0.0.1", :new_email "<mailto:c@b.com|c@b.com>"} {:email "<mailto:B@b.com|B@b.com>", :ip "10.0.0.2", :new_email "<mailto:d@b.com|d@b.com>"}]

souenzzo 2020-10-07T13:47:37.449500Z

clojure.set solution

(let [colla [{:email "<mailto:A@b.com|A@b.com>" :ip "10.0.0.1"}
             {:email "<mailto:b@b.com|b@b.com>" :ip "10.0.0.2"}]
      collb [{:email "<mailto:a@b.com|a@b.com>" :new_email "<mailto:c@b.com|c@b.com>"} 
             {:email "<mailto:B@b.com|B@b.com>" :new_email "<mailto:d@b.com|d@b.com>"}]
      xf-lower-email (map (fn [x]
                            (if (string? (:email x)) 
                              (update x :email string/lower-case)
                              x)))]
  (clojure.set/join (into #{} xf-lower-email colla)
                    (into #{} xf-lower-email collb)
                    {:email :email}))

2020-10-02T18:11:41.120Z

what do you want to happen in the event that the maps share the same key? i think with merge, the last value in wins

2020-10-02T18:12:36.120300Z

there's also merge-with

2020-10-02T18:12:59.121Z

which, i believe, lets you pass in an explicit merging strategy

ghadi 2020-10-02T18:13:42.121800Z

merging lists is not a thing. You can concatenate lists, or merge maps

ghadi 2020-10-02T18:14:09.122600Z

assuming you mean "merge maps repeatedly", @michael740’s question is relevant -- what is the desired behavior with two maps?

ghadi 2020-10-02T18:14:49.123600Z

and if that's a bad assumption -- perhaps an example of what you want would clarify

Michael W 2020-10-02T18:16:54.125100Z

I updated the question with the output from the working solution, It works fine, but it looks wrong to me, like I am missing a fundamental core function that would simplify the operation.

2020-10-02T18:17:58.126400Z

you can map over n collections, too

2020-10-02T18:18:14.126800Z

If these two vectors can be very long, and you are concerned about efficiency of this operation, then creating a map from the second vector, where the key is the :email value of the map, and the corresponding value is the map with the :email from the second vector, would be a good first step, so you do not need to do a linear scan of the second vector each time.

2020-10-02T18:19:00.127700Z

But given the output you want and the sample code you gave, I don't think there is a lot shorter Clojure code one could write using the core library of functions to achieve what you want.

2020-10-02T18:20:51.128900Z

And if the linear scan of the second vector is not a performance issue for you, then I would say that I don't consider the code you wrote ugly, personally.

2020-10-02T18:21:04.129Z

(map merge
     [{:name "michael"} {:name "stokley"}]
     [{:name "andy"}    {:name "fingerhut"}])
;; =&gt; ({:name "andy"} {:name "fingerhut"})

2020-10-02T18:21:22.129200Z

(map + [1 2] [3 4])
;; =&gt; (4 6)

2020-10-02T18:21:48.129400Z

@michael819 :man-shrugging: ^ ?

2020-10-02T18:22:46.130400Z

Another thing to note about your code is that if there was a map in the second vector with an :email that did not appear anywhere in the first vector, it would not be included in the result. Maybe you know that this is OK from the context of where you want to do this.

Michael W 2020-10-02T18:25:07.131200Z

That's actually intended, the actual maps have different sizes, and some may have an email while others don't.

Michael W 2020-10-02T18:26:05.132300Z

I just thought matching field values would be something the core would include but I didn't find anything in the docs and wanted to be sure I didn't miss anything. Still wrapping my head around functional programming. Thanks for the feedback.

Michael W 2020-10-02T18:26:10.132400Z

The vectors aren't the same size, and the map shape is slightly different.

2020-10-02T18:28:48.134600Z

You could perhaps consider writing a separate named function like (defn my-email [user] (string/lower-case (:email user))), and then call that twice:

(mapv (fn [user]
        (let [email (my-email user)]
          (merge i (first (filter #(= (my-email %) email) collb))))) colla)

Michael W 2020-10-02T18:30:13.135600Z

Yeah this was a one-off I didn't even write a function, just did in in the repl, but I would pull that out into a seperate function if I was going to prod with it.

ghadi 2020-10-02T18:32:10.138200Z

user=&gt; (group-by #(str/lower-case (:email %)) (concat colla collb))
{"<mailto:a@b.com|a@b.com>"
 [{:email "<mailto:A@b.com|A@b.com>", :ip "10.0.0.1"}
  {:email "<mailto:a@b.com|a@b.com>", :new_email "<mailto:c@b.com|c@b.com>"}],
 "<mailto:b@b.com|b@b.com>"
 [{:email "<mailto:b@b.com|b@b.com>", :ip "10.0.0.2"}
  {:email "<mailto:B@b.com|B@b.com>", :new_email "<mailto:d@b.com|d@b.com>"}]}

user=&gt; (map #(apply merge %) (vals *1))
({:email "<mailto:a@b.com|a@b.com>", :ip "10.0.0.1", :new_email "<mailto:c@b.com|c@b.com>"} {:email "<mailto:B@b.com|B@b.com>", :ip "10.0.0.2", :new_email "<mailto:d@b.com|d@b.com>"})
@michael819

ghadi 2020-10-02T18:32:28.138700Z

you can build up a lookup map by the normalized email

ghadi 2020-10-02T18:32:41.139100Z

then merge the right-hand side (the values) of the map

ghadi 2020-10-02T18:33:13.139900Z

still, better to normalize the emails at the outset

2020-10-02T18:33:17.140100Z

its a join

ghadi 2020-10-02T18:33:34.140700Z

also, ^. See clojure.set

2020-10-02T18:33:51.141Z

Doh! Of course. Still haven't had first coffee of the day.

2020-10-02T18:40:40.143700Z

a kind of generic hash-equi join algorithm for 2 collections is 1. index the smaller collection (this is the hash) 2. walk each item in the larger collection using the hash to find items in the smaller to join to

2020-10-02T18:40:57.144Z

which is kind of similar to the group-by above

2020-10-02T18:42:43.144800Z

https://github.com/clojure/clojure/blob/master/src/clj/clojure/set.clj#L111-L140 is an example which designed to work with sets, which means you lose any ordering you may want to preserve

dharrigan 2020-10-02T20:32:32.146300Z

For interop, if a method has setData(Map&lt;String, String&gt; data), is it as simple as doing (.setData object {"a" "b" "c" "d"})?

ghadi 2020-10-02T20:33:28.146700Z

yes @dharrigan, because clojure maps implement java.util.Map

dharrigan 2020-10-02T20:33:36.146900Z

w00t thank you 🙂

ghadi 2020-10-02T20:34:52.147200Z

dharrigan 2020-10-02T20:38:13.147600Z

yup, works flawlessly 🙂

dharrigan 2020-10-02T20:38:15.147800Z

fantastico

seancorfield 2020-10-02T20:46:36.149100Z

@ghadi That begs a question which often floats around in my mind: do you tend to use class and supers rather than type and ancestors (and only switch to the latter pair when you want something outside the "type" (class) hierarchy)?

ghadi 2020-10-02T20:47:19.149800Z

I have used type/ancestors probably a couple times in 10 years

seancorfield 2020-10-02T20:47:33.150500Z

(and, related, does ClojureScript have class/`supers`?)

ghadi 2020-10-02T20:47:36.150700Z

I'm usually interested in class hierarchy

ghadi 2020-10-02T20:49:13.151900Z

type and ancestors are built upon class / supers, so not as primitive

2020-10-02T20:49:51.152100Z

ClojureScript does not have class, only type

seancorfield 2020-10-02T20:52:36.152300Z

I just fired up a cljs REPL to check and I see it has ancestors but of course the types of a lot of things in cljs do not have any ancestors... 👀