Fork me on GitHub
#beginners
<
2019-04-12
>
tabidots05:04:43

I was trying to do something with a ridiculously large integer (bit-shift-right 360027784083079948259017962255826129 1) and got this bit operation not supported for: class clojure.lang.BigInt Is there any workaround, other than just using quot?

hiredman05:04:41

you can use java.math.BigInteger's shiftRight method

tabidots06:04:43

I saw that, but when I tried (BigInteger/shiftRight 360027784083079948259017962255826129 1) I got this No matching method shiftRight found taking 2 args for class java.math.BigInteger

jumar06:04:57

It's an instance method, not a static method. You have to call it like this (.shiftRight my-big-int 1)

tabidots06:04:38

Man, Clojure makes OOP feel so alien and confusing 😅

lennart.buit06:04:16

the “object” is the first argument, so always (.methodName obj arg arg arg)

lennart.buit06:04:29

It also works that way with Clojure protocols

tabidots08:04:16

I haven’t gotten that far yet 😅 Mostly doing math-y stuff. Haven’t had a need for anything fancy like protocols and polymorphism yet

holyjak06:04:41

Is there a smarter way to do the following, given a list of pairs:

[(apply min (map first pairs)) (apply max (map second pairs))]
? The thing is that this solution iterates pairs twice, which isn't optimal. Though maybe I am just prematurely ptimizing...

holyjak07:04:07

This is more efficient though hard to read

(->> [[1 2] [10 20]]
     (apply map (juxt min max))
     ((fn [[[mn _] [_ mx]]] [mn mx])))
; => [1 20]

hiredman07:04:32

(reduce (fn [m item] (-> m (update-in [:max] max item) (update-in [:min] min)))) {:max Long/MIN_VALUE :min Long/MAX_VALUE} pairs)

holyjak07:04:19

thank you but I would expect there must be a simpler way...

jr0cket09:04:37

If I have a structure such as '((\2 \1)) what function can I use to add \0 to create '((\0 \2 \1)). Thank you

jeroen.dejong09:04:12

(map #(conj % \0) '((\2 \1))) would work. although there’s probably a more elegant solution

jr0cket09:04:54

Thanks, that's elegant enough 🙂 I tweaked it to concat instead of conj as I realised I was adding (\0) rather than \0. All good now

igor24609:04:32

Hi folks! After running lein new app & lein uberjar i get two jars. What’s the difference between snapshot.jar and snapshot-standalone.jar?

rahul08032709:04:59

@igor246 The standalone jar is a jar with all the dependencies bundled in. The other one is just your code. The standalone one can run anywhere with a JVM but the normal ones needs your dependencies on the classpath

rahul08032709:04:17

You can see it in the size difference of the two jars

igor24609:04:13

Thank you!

jr0cket09:04:15

@igor246 snapshot-standalone.jar is the uberjar and includes the clojure library as well as your own application code, so can run without having the Clojure library available in the environment. For the snapshot you also need to include clojure library in the environment for it to run. https://github.com/technomancy/leiningen/blob/master/doc/TUTORIAL.md#uberjar

igor24609:04:29

Got it, thanks John!

victorbjelkholm42911:04:16

So I have the following snippet of code: https://gist.github.com/victorb/88f8137ab1b87dc0500f066bcce0406b It reads a stream via http that never ends. On every new item in the stream, it correctly calls the callback and the function itself is non-blocking. So far so good. However, I want the function to return a function that will close the stream when called. This also works correctly, but it seems that when I'm calling the function, (which in turn calls .close on the stream) it blocks until another item in the stream appear (but it never calls the callback, which is correct). What am I missing here? I want the stream to be closed immediately, not when the next item in the stream gets sent

henri.schmidt11:04:41

@victorbjelkholm429 I am pretty sure it is the behavior of with-open that is messing things up.

manutter5111:04:14

I was thinking the same thing, but now I’m looking at line-seq

victorbjelkholm42911:04:29

hm, I'll give it a try to handle it manually. Thanks @henri.schmidt

manutter5111:04:48

Yeah, you’re already handling the close manually, so you shouldn’t need with-open

victorbjelkholm42911:04:11

ok, changed it to the following:

(defn listen-for-changes [callback]
  (let [rdr ( (java.net.URL. url))]
    (future
      (doseq [line (line-seq rdr)]
        (callback (:id (json/read-json line true)))))
    (fn [] (.close rdr))))
Still, calling the returned function blocks until a new item appears in the stream

manutter5111:04:41

Ok, that’s what I was thinking might happen. line-seq calls .readline internally, and I’m guessing that the .close waits for .readline to return before actually closing.

manutter5111:04:22

(source line-seq)
(defn line-seq
  "Returns the lines of text from rdr as a lazy sequence of strings.
  rdr must implement java.io.BufferedReader."
  {:added "1.0"
   :static true}
  [^java.io.BufferedReader rdr]
  (when-let [line (.readLine rdr)]
    (cons line (lazy-seq (line-seq rdr)))))

manutter5111:04:14

You might do better rolling your own interruptable version of line-seq

victorbjelkholm42911:04:59

I see. Thanks a lot for digging in and helping out @manutter51!

victorbjelkholm42911:04:12

Ended up wrapping the .close in a future instead, so users of the lib (probably just me) can decide if they want to wait for the stream to properly close or not.

manutter5111:04:56

That’ll work 🙂

victorbjelkholm42911:04:23

thanks for the guidance still!

manutter5111:04:34

Now you got me curious about how to write a non-blocking line-seq.

victorbjelkholm42912:04:18

if you do manage to find out, please let me know

victorbjelkholm42912:04:06

I got stuck at ".readLine is supposed to abort if the stream is closed, but the stream doesn't close because .readLine is trying to read"

manutter5112:04:54

I’m thinking maybe something like this:

(defn get-next-line
  [^java.io.BufferedReader rdr interval]
  (while (not (.ready rdr))
    (Thread/sleep interval))
  (.readLine rdr))

(defn line-seq-non-blocking
  [^java.io.BufferedReader rdr]
  (when-let [line (get-next-line rdr 100)]
    (cons line (lazy-seq (line-seq-non-blocking rdr)))))

victorbjelkholm42913:04:58

Huh, makes sense. I'll give that a try later. Thanks!

sagar16jadhav13:04:17

(def counter 1) (doall (for [res getImageId] (do (def setImageID (split-numbers (res :id_image))) ) ;; counter needs to be increased how to inceremet counter ) ) how to set the counter increment in doall

manutter5113:04:07

@sagar16jadhav check out map-indexed for a way to turn a list into a numbered list. Also def inside a function probably isn’t doing what you want. Think of def as defining a global constant. You want it at the top level, and you want to def things only once.

manutter5113:04:06

If you just want to count how many imageID’s you have, you can use the built-in function count.

scotto13:04:18

Hi folks. I’m still a bit of a Clojure noob, and I’m trying to figure out the Clojure way to do a lookup in a hash table… I have a table like the following:

(def people
  [{:name "Anne" :id 1 :sort 1 :system true}
{:name "Barry" :id 2 :sort 1 :system false}
{:name "Chris" :id 3 :sort 1 :system true}])

(defn lookup-people [num]   ??????)

(lookup-people 2)
=> "Barry"
Can you please help me with the magic inside the ??????, or point me in the right direction?

scotto13:04:18

Hi folks. I’m still a bit of a Clojure noob, and I’m trying to figure out the Clojure way to do a lookup in a hash table… I have a table like the following:

(def people
  [{:name "Anne" :id 1 :sort 1 :system true}
{:name "Barry" :id 2 :sort 1 :system false}
{:name "Chris" :id 3 :sort 1 :system true}])

(defn lookup-people [num]   ??????)

(lookup-people 2)
=> "Barry"
Can you please help me with the magic inside the ??????, or point me in the right direction?

rahul08032713:04:36

(defn lookup-people [num]
  (->> people
       (filter #(= (:id %) num))
       (map :name)
       (first)))

k.i.o13:04:23

you also may consider passing people as argument, renaming num to id

rahul08032713:04:58

yes that would be better

rahul08032713:04:16

(defn lookup-people 
  [people num]
  (->> people
       (filter #(= (:id %) num))
       (map :name)
       (first)))

scotto13:04:08

Thank you both so much!

michael.e.loughlin13:04:27

it works because the key names (`:id` and :name) are used as the lookup function on the hash map

scotto13:04:01

I’m still trying to wrap my brain around ->>. Looking it up now…

k.i.o14:04:06

its very simple, taking first result and passing it as second parameter for next expression, repeats

scotto14:04:14

Ahhh, it’s the reverse of -> but puts it last…

k.i.o14:04:25

yeah thread first and thread last

michael.e.loughlin14:04:41

there's quite a lot of Clojure stuff going on in lookup-people to be fair threading macros, transducers, key-as-functions, lambda function shorthand

k.i.o14:04:02

if i remember correctly clojure core lib takes objects first and collections last hence -> ->>

orestis16:04:20

Nitpick: there’s no transducers here, just a plain map application. It looks like a transducer, E.g. (map :name) but the thread-last macro is rewriting it.

manutter5113:04:34

@scotto When I have to do something like that, I usually convert the list into a hash-map.

(defn by-id
  [coll]
  (reduce (fn [m i]
            (let [k (:id i)]
              (assoc m k i)))
          {}
          coll))
=> #'user/by-id
(get (by-id people) 2)
=> {:name "Barry", :id 2, :sort 1, :system false}

manutter5113:04:43

If you’re going to be doing a lot of lookups, it’s handy to just convert to hash-map once and keep the result for future lookups.

k.i.o14:04:39

@scotto you can even provide :id is a key argument in above example (defn by-key [key coll] ...)

michael.e.loughlin14:04:27

related: why is using a keyword (:id {:id 1}) favoured over (get {:id 1} :id) ?

k.i.o14:04:59

clean to read

manutter5114:04:29

Yeah, it's just a shortcut, for convenience.

michael.e.loughlin14:04:32

ah! and if you wanted to pass it as a higher order function, it gets much shorter

manutter5114:04:25

Yeah, it’s handy.

manutter5114:04:48

Fun fact: I recently found out you can do this:

(:foo {:bar 1} :key-not-found)
=> :key-not-found
(:foo {:bar 1} :some-default-value)
=> :some-default-value

lee18:04:22

I recently did too, but be sure this is what you want when the key is present with a nil value:

user=> (:foo {:foo nil} :key-not-found)
nil

manutter5118:04:37

Yep, I’ve been bitten by that one more than once. Caveat coder.

lee18:04:26

Bit me this morning!

scotto14:04:30

OK, because of @alexmiller I’m looking up into. At https:http://clojuredocs.org/clojure.core/into the first example shows

; When maps are the input source, they convert into an unordered sequence 
; of key-value pairs, encoded as 2-vectors
(into [] {1 2, 3 4})
=> [[1, 2] [3, 4]]
However, I’m getting a “null pointer exception” in my REPL when I try that.

k.i.o14:04:36

@scotto sounds strange, can you post repl output to pastebin?

scotto14:04:13

(into [] {1, 2} {3, 4})
NullPointerException   clojure.core.protocols/iter-reduce (protocols.clj:49)

k.i.o14:04:40

(into [] {1 2 3 4})

k.i.o14:04:04

(into [] {:a 1 :b 4})

alexmiller14:04:32

into takes 1 source collection

scotto14:04:41

OK, without the comma it works.

dpsutton14:04:50

{1, 2} {3, 4} vs {1 2, 3 4}

alexmiller14:04:53

commas are whitespace, so that's not the difference

scotto14:04:09

I was trying to mimic the example from the clojuredocs page.

k.i.o14:04:20

@scotto your mistake was two maps {1 2} {3 4} as input

scotto14:04:24

(into [] {1 2 3 4})
=> [[1 2] [3 4]]
(into [] {1, 2} {3, 4})
NullPointerException   clojure.core.protocols/iter-reduce (protocols.clj:49)

scotto14:04:56

ahhhhh, I see. Thanks.

scotto14:04:29

It’s important to transcribe the syntax properly. 😱

dpsutton14:04:37

same as "hello" "world" vs "hello world" 1 vs 2

scotto14:04:45

lol yes. Sorry

dpsutton14:04:06

no sorry 🙂 everyone in here signed up to help newcomers to the language

k.i.o14:04:17

@scotto mistakes are essential part of human existence, look at them as something unavoidable, smile and keep moving forward.

trevor67015:04:54

HI curious if it's possible/normal to destructure two hashsets passed into a clojure function? I'm not sure if I have the syntax right. I get an exception when trying it like below:

(def h1 {:x 23 :y 31})
(def h2 {:a 32 :b 44})

(defn h-add
  [
   [{:keys [x y]}]
   [{:keys [a b]}]
   ]
  (+ x y a b )
  )

(h-add h1 h2)
I get a `Syntax error (UnsupportedOperationException) compiling at (core.clj:26:1). nth not supported on this type: PersistentArrayMap`

trevor67015:04:28

very possible I have the syntax wrong, trying to get a better handle on how destructuring works in closure

alexmiller15:04:28

(defn h-add
  [{:keys [x y]} {:keys [a b]}]
  (+ x y a b))

trevor67015:04:15

Ha! that looks way better!

alexmiller15:04:26

and that is normal and idiomatic

trevor67015:04:30

I guess my confusion is around how the :keys symbol fits with everything else. I'm reading that as a "a hash map with a :keys element and a vector element containing a and b Does :keys get used in other contexts in clojure other than arg destructuring? How should I read that code in my head?

trevor67015:04:26

I know that with a function the first pair of [] is the args, another pair means we've destructured a list or vector (right?), do we use {} when working with hashmaps and sets? thanks!

trevor67015:04:53

If that's the case then this suddenly makes total sense, I just had to say it out loud in Slack I suppose

lilactown15:04:15

most of what you're saying is correct

trevor67015:04:02

Is this true?: Use [] to destructure a list, use {} to destructure a hashmap

lilactown15:04:05

{ } in destructuring is used for associative data. like maps, records, sorted-maps, and anything that implements the associative protocols

lilactown15:04:10

yes, that's true!

trevor67015:04:16

suddenly this makes total sense!

lilactown15:04:46

[] is used to destructure things that are sequential. things like lists, vectors

trevor67015:04:03

so the {} are notation saying "I'm destructing something associative", and :keys is just something that will get that associative data?

trevor67015:04:42

I could do other forms of access, but the {} denote 'associative destructing'?

lilactown15:04:53

:keys is a shorthand, saying "I want these keys from the associative data"

lilactown15:04:28

{:keys [a b]} will pull from {:a 1 :b 2} the values 1 and 2 and assign them to the local bindings a and b

trevor67015:04:16

fantastic. I could do other things to get the data, but they must be something that gets data via the keys. If I were to do something like h-add (vals h1) (vals h2) then I would destructure it with [] instead?

lilactown15:04:29

right. vals returns a seq so it can be destructured like [a b c d & xs]

trevor67015:04:09

Thank you so much @lilactown!

trevor67015:04:13

I think I get it!

orestis16:04:44

I have re-read the destructuring guide on the Clojure site so many times - eventually it clicks! 🤗 there’s so much cool stuff you can do with destructuring!

sfyire16:04:15

What does this mean?

didibus16:04:35

@sfyire What environment is this? VSCode ?

sfyire16:04:51

@didibus cursive

didibus16:04:59

Hum... It would seem that its failing to find clojure

didibus16:04:32

Do you have clojure installed?

didibus16:04:15

For example, see how you have to specify a path for clojure cli

mathpunk16:04:13

I'm trying to create a nested outline of some seqs of strings. I have the feeling that it's going to be a 3 line function that I could spend all day trying to find.

mathpunk16:04:34

The data looks like this:

(def stories
    '(("Node" "is defined")
      ("Node" "breadcrumb" "links to the parent process")
      ("Node" "breadcrumb" "links to the parent workflow")
      ("Node" "breadcrumb" "when following the parent process link" "reaches the expected process")))

mathpunk16:04:57

The desired output is something like this:

(def formatted-output
    [:h1 "Node" [:ul
                      [:li "is defined"]
                      [:li "breadcrumb" [:ul
                                         [:li "links to the parent process"]
                                         [:li "links to the parent workflow"]
                                         [:li "when following the parent process link"
                                          [:ul
                                           [:li "reaches the expected process"]]]]]]])

mathpunk16:04:34

And this looks like it kinda helps but, it only does one level: (update (group-by first stories) "Node" #(map rest %))

mathpunk16:04:26

I'm thinking, I probably need to make a case for leaf node vs non-leaf

mathpunk16:04:38

and build up from there

diegoemericksousa16:04:38

guys, how to access java enum in clojure ?

alexmiller17:04:17

@mathpunk clojure.walk/post-walk is where you should head

sfyire18:04:34

What's a good JDK version for a beginner to have installed?

ghadi18:04:41

Either 8 or 11

sfyire18:04:04

does clojure have problems with 11.0.2?

ghadi18:04:52

@sfyire can't speak for any user libraries though

dmarjenburgh18:04:52

@diegoemericksousa An enum declaration inside a class is an inner class. You have to use the $ syntax to access it. For example: Locale$Category/DISPLAY

noisesmith18:04:34

and the $ isn't a separator, it's part of the name

noisesmith18:04:55

so import of Locale doesn't also import Locale$Category, that's a separate class

diegoemericksousa19:04:42

it worked, thanks @noisesmith

afoltzm19:04:24

What's the idiomatic way to return a function that's the result of applying a default argument to the rightmost argument of another function? Something like (partial) but working like:

(partial-right clojure.string #" ")
Which would return a 1-arg function that splits the passed string on space. Is there something equivalent to (partial) for this?

hiredman20:04:37

#(whatever % some-arg)

afoltzm20:04:37

I figured that might be the answer. Is there a reason behind why partial only works one way but not the other?

lilactown20:04:52

Things might get weird with variadic functions in the general case. Better to be explicit and not use it as a general pattern IMO

hiredman20:04:24

partial, if I recall, pre-dates the # syntax sugar, so it is possible if those happened in the other order partial wouldn't be in clojure.core at all

afoltzm20:04:06

Gotcha. Thanks for the insight!

alexmiller20:04:24

In Clojure, anonymous functions are considered to be more idiomatic than partial

lee22:04:40

So you are partial to anonymous functions? 🙂

mathpunk23:04:20

after some experimentation I have determined, I definitely do not know how to use postwalk

mathpunk23:04:43

i was kinda hoping I'd get somewhere with what might be mutual recursion, but, it doesn't quite get to the bottom of my tree

mathpunk23:04:17

(def stories
    '(("Node" "is defined")
      ("Node" "breadcrumb" "links to the parent process")
      ("Node" "breadcrumb" "links to the parent workflow")
      ("Node" "breadcrumb" "when following the parent process link" "reaches the expected process")
      ("Workflow" "is defined")))

  (defn leaf? [parts]
    (= 1 (count parts)))

  (defn datafy-forest [forest]
    (map (fn [[label parts]]
           (if (leaf? parts)
             [:li label]
             (datafy-tree {label parts}))) forest))

  (defn datafy-tree [tree]
    (let [[label parts] (first (seq tree))]
      [:li label [:ul (datafy-forest (group-by first (map rest parts)))]]))

  (let [tree (dissoc (group-by first stories) "Workflow")]
    (datafy-tree tree))

  (let [tree (dissoc (group-by first stories) "Node")]
    (datafy-tree tree))

mathpunk23:04:41

close! I thought I might have it.

mathpunk23:04:48

I'll pick it up again tomorrow

mathpunk23:04:36

the output of the last one looks so promising...

[:li
 "Node"
 [:ul
  ([:li "is defined"]
   [:li
    "breadcrumb"
    [:ul
     ([:li "links to the parent process"]
      [:li "links to the parent workflow"]
      [:li "when following the parent process link"])]])]]

noisesmith23:04:42

you aren't using postwalk?

mathpunk23:04:44

but if it were correct, that last one would also be nested

mathpunk23:04:55

i couldn't understand what it does

mathpunk23:04:05

lemme show you me holding it wrong...

noisesmith23:04:23

it walks all the way to the bottom of the tree, then replaces each thing on the way back up using your function

mathpunk23:04:41

here's the last thing i tried before i gave up on it: my idea was, maybe forget the data, just see if I can classify different nodes correctly, so the shape is what it I'd expect

mathpunk23:04:44

(defn classify [node]
    (cond
      (string? node) :label
      (= 2 (count node)) :leaf
      :else :story))

  (postwalk classify (group-by first stories))

noisesmith23:04:20

so that's going to fail as it tries to replace collections with keywords

noisesmith23:04:38

if it gets a thing that holds an object, you probably want to return a new thing that also holds similar things

noisesmith23:04:06

perhaps not fail but rather return something useless

mathpunk23:04:22

i'll come back to it.... I'm sure it's the right thing but I tried to get it to return, like, anything for an hour

mathpunk23:04:38

i have a completely wrong mental model of what it does and couldn't really make heads or tails of the examples i found

noisesmith23:04:02

user=> (clojure.walk/postwalk (fn [node] (if (map? node) (assoc node :foo 42) node)) {:a 0 :b {:x 12 :y [{} {} {}]}})
{:a 0, :b {:x 12, :y [{:foo 42} {:foo 42} {:foo 42}], :foo 42}, :foo 42}

noisesmith23:04:25

it literally returns the thing that replaces each thing it finds in a tree, bottom up

mathpunk23:04:27

here's another attempt

mathpunk23:04:30

(postwalk (fn [m] (if (leaf? m)
                        [:li m]
                        [:ul [:li (first m)]])) (group-by first stories))

noisesmith23:04:43

that's much closer to reasonable, yes

noisesmith23:04:18

do you never want the rest of m if it's not a leaf?

mathpunk23:04:34

i totally do

noisesmith23:04:24

so you should do something with (rest m) in that code - any data not in what your function returns is not in the result of postwalk

mathpunk23:04:48

i can't even parse English at this point 🙂 I will read what you just said tomorrow, thanks

mathpunk23:04:15

I am fully committed to THE WAY OF THE TREE

noisesmith23:04:24

the idea (if this is clearer) is that (first m) implies "I don't want anything from m other than the first item"