clojure

New to Clojure? Try the #beginners channel. Official docs: https://clojure.org/ Searchable message archives: https://clojurians-log.clojureverse.org/
caumond 2021-02-05T05:55:54.222900Z

In case your conditions to navigate are getting more and more complex, have a look to specter : https://github.com/redplanetlabs/specter

2021-02-05T07:41:02.226100Z

Currently I have a function like follows:

(cond
  (-> db/query-a seq)
  (-> db/query-a first process)

  (-> db/query-b seq)
  (-> gb/query-b first process)

  ...

  (-> db/query-n seq)
  (-> db/query-n first process)
However, this means that for every query, I have to make two trips to the database. What would be the best way to rewrite this in clojure so it only has to make one query per condition, and does not have to make the subsequent queries until knowing that the previous queries are empty?

2021-02-05T08:07:29.226400Z

thanks @p-himik! not-empty looks neat, I poked around for a cond-some and stumbled upon https://github.com/Engelberg/better-cond which I will give a go

nbardiuk 2021-02-05T09:24:28.226900Z

(condp #(-> (%1 %2) seq) db
    db/query-a :>> #(-> % first process)
    db/query-b :>> #(-> % first process))
https://clojuredocs.org/clojure.core/condp has an overload that allows to pass result of condition to the function

2021-02-05T10:42:46.227600Z

@nbardiuk That's perfect! It will take me a while to parse through but I will try to use it - good learning op πŸ˜„

2021-02-05T10:42:49.227800Z

Thanks!

2021-02-05T17:09:12.237600Z

yeah, condp is a little weird (eg. how it scrambles the args provided), but can really DRY up code

2021-02-05T17:13:48.237800Z

also, this pattern could be replaced by "or", since the condp function is pure glue with hardly any logic:

(process
  (first
    (or (seq db/query-a)
        (seq db/query-b)
        ...
        (seq db/query-n)))))
(first already implicitly calls seq, but it's safe to call it twice)

πŸ‘ 1
2021-02-05T17:15:35.238100Z

but you would want to stop if none were true, but that's a simple flip

(some-> (some seq [query-a query-b ... query-n])
        (first)
        (process))

2021-02-05T17:15:59.238300Z

(I also realized a coll plus some seq is simpler than calling seq on each item)

p-himik 2021-02-05T07:52:02.226200Z

(if-some [qa (not-empty db/query-a)]
  (-> qa first process)
  (if-some [qb (not-empty db/query-b)]
    (-> qb first process)
    ...))

Falco 2021-02-05T11:35:05.228400Z

I need your help. I do remember a Clojure environment mentioned in a blog post. As far as I recall this was a standalone environment (single JAR?) and praised by the author for its simplicity and beginner friendliness, because it meant that you could instantly start coding from anywhere without a full-blown IDE. Does this ring a bell with anyone? Is it possible that this tool has been shipped with the official Clojure distribution in earlier times? It might also have been part of another tool, or maybe ClojureScript. I tried googling a lot, but to no avail ("clojure gui" is a pretty overloaded search term -.- )

tvirolai 2021-02-05T11:36:25.228500Z

Klipse? https://github.com/viebel/klipse

bronsa 2021-02-05T11:40:10.228900Z

https://github.com/oakes/Nightlight (successor to https://github.com/oakes/Nightcode which is probably the one you were thinking of)

Falco 2021-02-05T11:40:41.229100Z

Looks interesting, thank you. But somehow I remember having a native app.

Falco 2021-02-05T11:42:53.229500Z

Thanks, that is indeed a good candidate... if my memory just wasn't so blurry πŸ™ˆ

javahippie 2021-02-05T11:50:04.229800Z

When it was some time back, maybe it was LightTable? http://lighttable.com

Falco 2021-02-05T11:55:08.230100Z

@javahippie thanks for brining it up πŸ‘

Shantanu Kumar 2021-02-05T12:28:38.230500Z

Both repos are archived on Github

Quentin Le Guennec 2021-02-05T15:22:51.231800Z

Hello, hash on a zipper returns a different value at each call. Does anyone know why?

Quentin Le Guennec 2021-02-05T15:23:50.232200Z

(a consequence of this is that = isn't consistent either)

p-himik 2021-02-05T15:26:58.232300Z

If you define your functions inline, that's probably why:

cljs.user=> (= (fn []) (fn []))
false
cljs.user=> (hash (fn []))
1
cljs.user=> (hash (fn []))
2

Quentin Le Guennec 2021-02-05T15:27:22.232500Z

Yes, I figured it out. I think that's the reason.

2021-02-05T15:41:13.232700Z

= and hash on Clojure functions are based on identity of that function object in memory.

Quentin Le Guennec 2021-02-05T15:41:33.232900Z

Yeah, that makes sense.

2021-02-05T15:45:16.233100Z

I have not looked at the implementations of zippers in Clojure carefully -- are you saying that they involve storing reference to Clojure functions inside of the data structure?

Quentin Le Guennec 2021-02-05T15:45:27.233300Z

Yes.

2021-02-05T15:46:06.233500Z

Hmmm. OK. I somehow thought they had ways of representing them without doing that -- but again, I have never looked closely.

Quentin Le Guennec 2021-02-05T15:46:25.233700Z

Oh no, sorry, misread your question

Quentin Le Guennec 2021-02-05T15:46:45.233900Z

I am myself storing functions in the zipper, that doesn't have anything to do with the implementation itself

2021-02-05T15:47:26.234100Z

OK. Doing that will cause the same issues of those functions having = and hash based on identity, no matter which immutable collections you make them a part of in Clojure.

Quentin Le Guennec 2021-02-05T15:47:45.234300Z

Yes

Quentin Le Guennec 2021-02-05T15:48:14.234600Z

a function reference defined with def should work though?

2021-02-05T15:48:37.234800Z

Depends upon what you mean by "work".

Quentin Le Guennec 2021-02-05T15:48:52.235Z

I mean that = and hash should be consistent

2021-02-05T15:49:06.235200Z

If I do (defn foo [x] (inc x)) followed by (def bar foo), then (identical? foo bar) should be true.

Quentin Le Guennec 2021-02-05T15:49:18.235400Z

I see

2021-02-05T15:49:45.235600Z

If I do (def foo (fn [x] (inc x))) followed by (def bar (fn [x] (inc x))) then (identical? foo bar) should be false.

2021-02-05T15:50:26.235800Z

Each invocation of defn or fn creates a new function object.

Quentin Le Guennec 2021-02-05T15:50:36.236Z

yes

2021-02-05T15:52:07.236200Z

There might be exceptions to that statement, but I'm hard pressed to think of one, and relying on such behavior seems very fragile to me.

Quentin Le Guennec 2021-02-05T15:52:44.236400Z

yeah, a function identifier and a identifier->function map seems more reliable

2021-02-05T15:54:16.236600Z

Using an immutable value like a string, keyword, or symbol as an identifier -- definitely.

Quentin Le Guennec 2021-02-05T15:54:26.236800Z

Yes

2021-02-05T15:56:41.237Z

If it matters to you to be able to distinguish such immutable values by some kind of 'type' from other immutable values, you could even create a new type with defrecord that has one field that is the string/keyword/symbol, but that might be overkill for your needs.

2021-02-05T16:48:40.237200Z

yeah, light table got a bunch of buzz and had some high quality talks hyping it, but the project seems to have fizzled

2021-02-05T16:49:25.237400Z

oh, it's still under development

2021-02-05T17:36:18.239800Z

Hi, I think I’ve hit a bug when using compojure-api with ring-swagger. When using a custom JSON encoder the swagger.json file also changes, rendering the spec invalid. More details here https://github.com/metosin/compojure-api/issues/449 - any ideas?

takis_ 2021-02-05T17:59:03.240400Z

hello ,

ITransientMap tm = (ITransientMap) transientC.invoke(PersistentHashMap.EMPTY);
and many assoc after using java interop , works

takis_ 2021-02-05T17:59:21.240700Z

ITransientMap tm = (ITransientMap) transientC.invoke(PersistentArrayMap.EMPTY);

takis_ 2021-02-05T17:59:35.241100Z

and many assoc after using java interop , doesnt work

takis_ 2021-02-05T18:01:55.242300Z

i am using them in wrong way? i know that ArrayMap becomes auto HashMap

takis_ 2021-02-05T18:04:00.243700Z

its not a problem i just used the HashMap,but why ArrayMap didnt work? only some members added,2 keys that had big values didnt added

2021-02-05T18:04:51.244500Z

@takis_ transients only accidentally update in place, the correct way to use them is to use the return value of the updating function in place of the original object

2021-02-05T18:05:24.245200Z

as you've seen, adding to it does work as mutation for a few thing, until it stops working

takis_ 2021-02-05T18:06:16.246300Z

oh its my fault, i knew that transients works as persistent,i will retry it,i forgot the assign again

takis_ 2021-02-05T18:07:31.246800Z

yes now its fine it was bug i forgot it , thank you noisesmith πŸ™‚

takis_ 2021-02-05T18:08:05.247100Z

for perfomance reasons,what to use?

takis_ 2021-02-05T18:08:17.247400Z

i should start with ArrayMap or HashMap?

takis_ 2021-02-05T18:08:51.248Z

some maps can be very small in my app even 2 members

takis_ 2021-02-05T18:09:32.248500Z

anyway i will test it i guess ArrayMap better , thanks :)

2021-02-05T18:16:28.249100Z

ArrayMap tends to work best for small sizes (and auto-promotes to HashMap when it gets larger)

vemv 2021-02-05T19:10:45.251600Z

I'm playing a bit with compile. If I (compile 'rebel-readline.main), a rebel_readline dir gets created inside classes, containing clojure (and likewise for other transitive deps):

$ ls classes/rebel_readline
clojure/                                                               jline_api$char_at.class
...
Is that expected behavior? It seems odd to me as a more normal structure would be to have clojure and rebel_readline as sibling dirs, inside classes

borkdude 2021-02-05T19:12:14.252100Z

@vemv it's probably expected since rebel-readline has namespaces with clojure in it: https://github.com/bhauman/rebel-readline/tree/master/rebel-readline/src/rebel_readline/clojure

borkdude 2021-02-05T19:12:48.252700Z

(which is maybe not allowed by our bd, but that's a different issue)

vemv 2021-02-05T19:13:09.253Z

ahhhh that confused me lots :) I thought I was looking at the real clojure.main

borkdude 2021-02-05T19:13:31.253200Z

clojure main is probably not compiled since there is already a compiled version on the classpath while you're compiling

πŸ‘ 1
alexmiller 2021-02-05T19:15:23.253500Z

correct

Jeremy Cronk 2021-02-05T19:57:07.256800Z

If I’m dealing with a collection of Java objects, say [i1 i2 i3 i4] and I need to do something like (.setResult i1 i2) then (.setResult i2 i3), etc., what’s the best way to do that? It looks like a reduction to me, but it’s only being done for side effects, so it doesn’t need to return anything. I think I’m just confusing myself at this point, because it seems like there should be a simple way to do this. Any suggestions?

phronmophobic 2021-02-05T20:00:13.257900Z

you can use run!:

(run! (fn [[a b]] (.setResult a b)) 
      (map vector coll (rest coll)))

πŸ‘ 1
Jeremy Cronk 2021-02-05T20:02:11.258100Z

Yes! This is the half-remembered function I was searching for. Thanks!

πŸ‘ 1
2021-02-05T20:18:40.259200Z

with doseq you don't need the fn

(doseq [[a b] (partition 2 1 coll)]
  (.setResult a b))

Quentin Le Guennec 2021-02-05T20:46:25.260500Z

Hey, will = be perform in O(1) time if the output of hash function for its arguments is the same?

dpsutton 2021-02-05T20:48:13.261800Z

i believe by the pigeonhole principle it cannot. If we admit there are an infinite amount of hashable items and a finite number of hashes, hash would destroy equality if used in this manner.

Quentin Le Guennec 2021-02-05T20:50:00.262Z

I see

Quentin Le Guennec 2021-02-05T20:50:45.262300Z

That would make sense, but it's also very unlikely

Quentin Le Guennec 2021-02-05T20:51:13.262600Z

@andy.fingerhut?

dpsutton 2021-02-05T20:53:29.262900Z

(count (into #{} [(reify Object (hashCode [_] 42)) (reify Object (hashCode [_] 42))]))

2021-02-05T20:53:36.263Z

There are only 2^32 different 32-bit hash values.

2021-02-05T20:53:59.263200Z

There are far more than 2^32 possible Clojure values that can be hashed and used as a key in a hash map, or an element in a hash set.

Quentin Le Guennec 2021-02-05T20:54:07.263400Z

Indeed

2021-02-05T20:54:09.263600Z

Many pairs of such values must have the same hash value.

Quentin Le Guennec 2021-02-05T20:54:35.263800Z

So I guess the answer is no.

2021-02-05T20:54:39.264Z

If hash is implemented correctly, it is safe to assume that (hash x) being different than (hash y) implies that x is not equal to y

dpsutton 2021-02-05T20:54:41.264200Z

and (= (reify Object (hashCode [_] 42)) (reify Object (hashCode [_] 42))) is a direct answer

2021-02-05T20:55:01.264400Z

If hash is implemented incorrectly, then even that assumption is not safe.

2021-02-05T20:57:04.264600Z

Regarding "very unlikely" if hash is "evenly distributed" or you use "random values" then with 2^32 possible values, there is fun thing called "Birthday Paradox" (not really a paradox, really more of a 'surprising non-obvious fact') that says whenever you have about the square root of the number of "hash buckets" (2^32 square root is 2^16 = 65,536), then you are 50% likely to have a collision.

πŸ‘ 1
2021-02-05T20:57:29.264800Z

That is for "uniform randomly distributed values" -- it is of course possible to know more about your data set and how they are distributed, that could change that likelihood of collision.

Quentin Le Guennec 2021-02-05T20:57:59.265Z

Hmm I see, yes I heard about that before

2021-02-05T20:58:36.265200Z

So if you have a hash map or hash set with 65,536 items, about half the time at least two of those will collide in their hash values, and Clojure's implementation will put such items in a linear linked list.

2021-02-05T20:58:54.265400Z

(a separate linear linked list per hash value, not one common linked list for the hash-map/hash-set as a whole)

Quentin Le Guennec 2021-02-05T20:58:58.265600Z

Oh ok, that's interesting

2021-02-05T20:59:52.265800Z

That Birthday Paradox is one of the reasons that UUIDs have as many bits as they do. 32 bits is far too small for the use case that UUIDs are for.

Quentin Le Guennec 2021-02-05T20:59:55.266Z

So that means the colliding values in a hashset are tested not against their hash, but in a O(n) comparison

2021-02-05T21:00:37.266200Z

The colliding values in a hash set all have equal hash values, so they cannot be told apart by their hash values alone. You have to do a linear search and do clojure.core/= on the searched value and the ones in the collection that collide.

2021-02-05T21:01:14.266400Z

Even if there are no collisions, you have to do clojure.core/= on a searched-for value and the one in the collection with the same hash, because even though they have the same hash, they might return false for clojure.core/=

Quentin Le Guennec 2021-02-05T21:01:27.266600Z

Yes