clojure

New to Clojure? Try the #beginners channel. Official docs: https://clojure.org/ Searchable message archives: https://clojurians-log.clojureverse.org/
seancorfield 2020-09-21T00:09:17.170800Z

@joao.galrito Welcome! If you're just getting started with Clojure, ask lots of questions in the #beginners channel -- that's where folks have opted in to helping new Clojurians and you'll find lots of willing minds to pick there.

seancorfield 2020-09-21T00:11:19.171Z

If you have questions about specific libraries, there are likely library-specific channels where you can ask those questions (e.g., #sql for database stuff, #honeysql #hugsql for specific ilbraries, #ring #component #integrant #hiccup etc -- some of them can be pretty quiet but that means there's also more chance that an expert can help there).

João Galrito 2020-09-21T00:50:28.171200Z

@seancorfield thank you. I'd say I'm past the beginner phase. In this case I'm looking for an opinion and some tips/suggestions on a project I'm working on. Mainly if my approach and use of the language is sound

emccue 2020-09-21T00:54:52.171500Z

hit me

suren 2020-09-21T01:16:21.172Z

Whats the preferred package for TDD in clojure?

seancorfield 2020-09-21T02:41:18.172700Z

@suren clojure.test is built-in. Most people use that. Works well with all the editors and command-line tooling out there.

suren 2020-09-21T11:28:41.174Z

Thanks buddy

practicalli-john 2020-09-23T11:37:51.021400Z

http://practicalli.github.io/clojure/testing/unit-testing/ is an intro to unit testing in Clojure with some example projects.

restenb 2020-09-21T11:42:18.176100Z

so I know you can replace vector elements simply with assoc: (assoc [1 2 3] 1 4)->`[1 4 3]` f.ex. But what's a simple way to, given a list of indexes and a list of values, replace all indexes in that vector with the corresponding value?

restenb 2020-09-21T11:42:37.176400Z

I can feel there must be a simple solution to this but my brain is stuck ... 😛

jsn 2020-09-21T11:45:26.176700Z

(reduce (partial apply assoc) [1 2 3 4 5 6] [[1 :a] [3 :b]]) would work, but that's probably not what you mean

nivekuil 2020-09-21T11:48:52.176800Z

https://clojuredocs.org/clojure.core/replace but the list of values there is also the list of indexes

2020-09-21T11:48:54.176900Z

(apply assoc [1 2 3] (interleave '(0 2) '(:x :y)))

2020-09-21T11:49:22.177100Z

where '(0 2) is for list of indices and '(:x :y) for list of values

2020-09-21T11:50:49.177300Z

if you need to catch IndexOutOfBound exception you can use loop or reduce instead of apply for more precise control

✔️ 1
restenb 2020-09-21T11:53:50.177800Z

replacelooks promising but I can't make heads or tails of those examples

restenb 2020-09-21T11:55:15.179Z

(replace [10 9 8 7 6] [0 2 4])->`[10 8 6]` so it just kept index 0, 2, and 4?

nivekuil 2020-09-21T11:55:58.179300Z

In the top example, the first argument shows how it is a map of index to keyword/value

restenb 2020-09-21T11:58:24.180400Z

i don't know what's more obtuse about this, the docstring or the examples

restenb 2020-09-21T11:59:08.180900Z

also when you supply replacement pairs as in the second example, it seems to replace by value, not by index

restenb 2020-09-21T11:59:19.181200Z

so still not clear to me how to replace by index

alexmiller 2020-09-21T13:04:48.184200Z

replace works on the values, not the indexes so doesn't do what you want. I don't think there is a function to do this for you, but you can use reduce or a loop to repeatedly assoc

jimmy 2020-09-21T16:48:47.189Z

I agree that these examples are confusing. Here is are two equivalent statements for replace that maybe help clarify.

(replace {0 10 
          1 9
          2 8
          3 7
          4 6}
         [0 1 2 3 4])

(replace [10 9 8 7 6]
         [0 1 2 3 4])
You can think about a vector as a map from indexes to values. That is what the example is taking advantage of. Replace is looking up its value in the map that is provided to find its replacement. So in this case, you can think of the code like this
(let [replacements [10 9 8 7 6]
      my-coll [0 1 2 3 4]]
  (mapv 
   (fn [x] (or (get replacements x) x)) 
   my-coll))
I know that doesn't help solve your problem. Just wanted to explain what was going on.

emccue 2020-09-21T17:53:08.189800Z

@restenb

emccue 2020-09-21T17:53:11.189900Z

(defn replace-at-indexes
  "(replace-at-indexes ['a 'b 'c] [[0 'f] [1 'd]])
    => [f d c]"
  [vector index-replacement-pairs]
  (loop [pairs index-replacement-pairs
         vector vector]
    (if (empty? pairs)
      vector
      (let [[idx val] (first pairs)]
        (when (>= idx (count vector))
          (throw (IndexOutOfBoundsException.)))
        (recur (rest pairs)
               (assoc vector idx val))))))

emccue 2020-09-21T17:53:16.190100Z

here ya go

emccue 2020-09-21T17:53:38.190500Z

the when there is to normalize one of the more odd behaviors of assoc

emccue 2020-09-21T17:54:28.190900Z

(assoc ['a] 0 'b)
=> [b]
(assoc ['a] 1 'b)
=> [a b]
(assoc ['a] 2 'b)
Execution error (IndexOutOfBoundsException) 

emccue 2020-09-21T17:56:57.191600Z

(replace-at-indexes [] [])
=> []
(replace-at-indexes ['a] [[0 'b]])
=> [b]
(replace-at-indexes ['a] [[1 'b]])
Execution error (IndexOutOfBoundsException)
(replace-at-indexes [] [])
=> []
(replace-at-indexes ['a] [[0 'b]])
=> [b]
(replace-at-indexes ['a] [[1 'b]])
Execution error (IndexOutOfBoundsException)

emccue 2020-09-21T17:57:11.192Z

some more examples of usage there to help you doodle out unit tests

nick 2020-09-21T18:44:55.194500Z

Any suggestions on how I can remove "duplicate key/values" from this map are greatly appreciated By "duplicate key/values" I mean pairs like [:foo 1] [:bar 2] & [:bar 2] [:foo 1]

(custom-distinct
    {[:foo 1]    [:bar 2]
     [:bar 2]    [:foo 1]
     [:foo 4539] [:google_play 1]
     })

  ;; expected:
  ;; {[:foo 1]    [:bar 2]
  ;;  [:foo 4539] [:google_play 1]}

nick 2020-09-21T18:58:04.194900Z

This is embarrassing the simplest reduce-kv seemed to be solve the issue.

(reduce-kv (fn [acc k v]
             (if (or (get acc k) (get acc v))
               acc
               (assoc acc k v)))
           {}
           {[:foo 1]    [:bar 2]
            [:bar 2]    [:foo 1]
            [:foo 4539] [:google_play 1]})

2020-09-21T18:59:42.195100Z

Why is the [:bar 2] key removed and not [:foo 1]? Your algorithm doesn't seem deterministic. (Maybe that doesn't matter in this case?)

emccue 2020-09-21T19:00:04.195500Z

(defn distinct-keys-and-values [m]
  (loop [keys-and-vals (seq m)
         new-map {}
         seen #{}]
    (if (empty? keys-and-vals)
      new-map
      (let [[k v] (first keys-and-vals)]
        (if (or (contains? seen k)
                (contains? seen v))
          (recur (rest keys-and-vals)
                 new-map
                 seen)
          (recur (rest keys-and-vals)
                 (assoc new-map k v)
                 (-> seen
                     (conj k)
                     (conj v))))))))

🙏 1
nick 2020-09-21T19:00:15.195600Z

yep, in my case it's not important. Thanks for checking!

👍 1
Eddie 2020-09-21T19:02:30.196800Z

Or you put the key-value pairs into an un-ordered collection and call distinct.

(into {} (map vec (distinct (map set m))))

🙏 1
Eddie 2020-09-21T19:03:14.197300Z

But you lose control over which key-value pair you preserve.

emccue 2020-09-21T19:03:30.197800Z

imo there are worse things than being explicit

emccue 2020-09-21T19:03:45.198200Z

so my loop example is the loser in terms of lines of code

emccue 2020-09-21T19:03:55.198500Z

but super clear what it does

Eddie 2020-09-21T19:04:10.198900Z

Agreed.

jsn 2020-09-21T19:04:23.199400Z

@emccue well, it also does the wrong thing, as far as I can see

jsn 2020-09-21T19:05:07.200700Z

so no, I disagree; I think the @erp12's variant is much more clear, instantly readable

Eddie 2020-09-21T19:05:35.201200Z

One could also argue that the original data doesn't belong in a map to begin with. If it doesn't matter which value is the key and which is the value, then I would think of the data as a set of unordered pairs.

jsn 2020-09-21T19:06:09.201600Z

yeah, a set of sets could be a better fit

jsn 2020-09-21T19:06:39.202500Z

(which you almost do construct anyway before turning it into a map)

👍 1
Eddie 2020-09-21T19:08:46.204900Z

As the for clarity of the one-liner, there is definitely ambiguity as to what will be included in the final map, which isn't great.... but on the other hand if we are considering [a b] to be a duplicate of [b a] then we are stating that we don't care.

Eddie 2020-09-21T19:09:33.206Z

There could be a use case for being more explicit about which pair you preserve, and that would require some kind of loop or reduce.

jsn 2020-09-21T19:11:33.207100Z

If 'which pair' is some kind of orderning, we could just enforce it in the one-liner, instead of map set , though

jsn 2020-09-21T19:11:39.207300Z

no need for loop for that

Eddie 2020-09-21T19:12:27.207900Z

Good point @jason358. (map sort m) could work in many cases.

Eddie 2020-09-21T19:13:03.208600Z

We also know that we couldn't rely on the "first-seen" or "last-seen" semantics because the input map has no order.

jsn 2020-09-21T19:15:18.209200Z

well, that would change the effect on non-duplicate pairs, though

👍 1
jsn 2020-09-21T19:15:39.209800Z

which brings us back to the question of if map structure is really a good fit 🙂

sparkofreason 2020-09-21T23:35:40.215Z

Hitting what I assume is a class loader issue. Trying to make a Google cloud function with JVM clojure, getting the error below. The class is created using gen-class, wondering if that isn't going to for the situation where the environment is using a different class loader.

"Exception in thread "main" java.lang.ExceptionInInitializerError
	at clojure.lang.Namespace.<init>(Namespace.java:34)
	at clojure.lang.Namespace.findOrCreate(Namespace.java:176)
	at clojure.lang.Var.internPrivate(Var.java:156)
	at ck.proxysql_notifier.<clinit>(Unknown Source)
	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)
	at com.google.cloud.functions.invoker.NewHttpFunctionExecutor.forClass(NewHttpFunctionExecutor.java:51)
	at com.google.cloud.functions.invoker.runner.Invoker.startServer(Invoker.java:243)
	at com.google.cloud.functions.invoker.runner.Invoker.main(Invoker.java:129)
Caused by: java.io.FileNotFoundException: Could not locate clojure/core__init.class, clojure/core.clj or clojure/core.cljc on classpath.
	at clojure.lang.RT.load(RT.java:462)
	at clojure.lang.RT.load(RT.java:424)
	at clojure.lang.RT.<clinit>(RT.java:338)
	... 11 more" 

alexmiller 2020-09-21T23:38:10.215300Z

that's just telling you it can't find clojure on the classpath

alexmiller 2020-09-21T23:38:43.215600Z

or whatever classloader is doing this load

sparkofreason 2020-09-21T23:48:32.216300Z

Clojure should be on the classpath. It is in the uberjar.

sparkofreason 2020-09-21T23:52:41.216800Z

I suspect it needs setContextClassLoader someplace.