beginners

Getting started with Clojure/ClojureScript? Welcome! Also try: https://ask.clojure.org. Check out resources at https://gist.github.com/yogthos/be323be0361c589570a6da4ccc85f58f.
Trung Dinh 2020-09-15T05:16:59.222800Z

Hi team, I’m trying to make concurrent calls to apis to gather paginated results, I’m thinking of using atom, future and loop for that, my take so far is as follows, but not sure how do I ensure not breaking out of loop before all future calls finish? Any advise please? Thanks

(let [panda-mappings (atom {})
      range 200]
  (loop [offset 0]
    (future (swap! panda-mappings assoc offset "val"))
    (if (> offset range)
      (println @panda-mappings)
      (recur (+ offset 50))))
  (println @panda-mappings))

cgrand 2020-09-15T07:19:29.225200Z

I second looking at pipeline-blocking. However the way you use swap! and future can’t work. If you want to use futures you must use them to perform the actual fetch and you have to store the future inside the map. Then once all futures started you have to wait.

👍 1
cgrand 2020-09-15T07:24:06.225400Z

(loop [futures {} [offset & offsets] (range 0 250 50)]
  (if offset
    (recur (assoc futures offset (future (DO-FETCH offset))) offsets)
    ; done, let's wait
    (into {} (map (fn [[o f]] [o @f])) futures)))

🦜 3
Trung Dinh 2020-09-15T08:45:02.225900Z

nice @cgrand, thanks for the idea of putting all the future in one collections and wait

Trung Dinh 2020-09-15T08:45:29.226100Z

my attempt was

(let [panda-mappings (atom {})
      range 200
      futures []]
  (loop [offset 0]
    (conj futures (future (swap! panda-mappings assoc offset "val")))
    (when (< offset range)
      (recur (+ offset 50))))
  (when (every? realized? futures)
    (println @panda-mappings)))

Trung Dinh 2020-09-15T08:46:13.226300Z

and it works 🙂 , will need to learn your example a bit more, seem a bit cryptic for me the map part…, anyway, thanks

baptiste-from-paris 2020-09-15T12:15:27.227500Z

@trung, completing @cgrand example, you *could use (deref ref timeout-ms timeout-val) with a timeout value. It’s not perfect as you don’t handle errors but it *might be better than nothing depending on your context

👍 1
2020-09-15T17:45:50.235300Z

many people find it counter-intuitive, but when I do side effects collecting results across a collection, I prefer reduce or into over loop since they bake in the traversal pattern

(into {}
      ;; wait on and deref futures via transducer
      (map (fn [[k v]]
             [k @v]))
      ;; eagerly start futures before further processing
      (mapv (fn [o]
              [o (future (DO-FETCH o))])
            (range 0 250 50)))

2020-09-15T17:46:37.235500Z

the whole thing turns into a few very common idioms (something I aim for)

Trung Dinh 2020-09-16T02:29:12.271700Z

cool @noisesmith, really like this idiomatic way, thanks. One more question please, when I try doing (map (fn [[k v]] @v)) instead of (map (fn [[k v]] [k @v])) , to get all results without the indices to the map, I get “Vector arg to map conj must be a pair” IllegalArgumentException, I’ve been mucking around without luck, any thoughts?

2020-09-16T02:31:58.271900Z

you can only conj a key/value pair to a hash map

2020-09-16T02:32:47.272100Z

replace the {} with #{} or [] and it works, if you didn't need the keys

1
Trung Dinh 2020-09-15T05:20:43.223600Z

results is

Trung Dinh 2020-09-15T05:21:04.224Z

I’m trying to get something like this instead…

{100 val, 200 val, 150 val, 250 val, 0 val, 50 val}

dpsutton 2020-09-15T05:30:12.224300Z

clojure.core.async has some ideas about how to communicate like this. you could check out clojure.core.async/pipeline-blocking

1
Trung Dinh 2020-09-15T05:38:45.224500Z

you mean future is not the right tool for this kind of problem?

dpsutton 2020-09-15T06:23:59.224800Z

there are many tools for this. but core.async can give you good coordination tools for your problem of waiting until all the requests finish

dpsutton 2020-09-15T06:24:26.225Z

pipeline-blocking will actually use thread underneath it but with async channels to communicate results and pending results

zackteo 2020-09-15T12:43:39.229100Z

Am following a Clojure Tutorial - Anyone has an idea why (shutdown-agents) causes .... : "Exception updating the ns-cache #error"? I can skip this but am just wondering o:

(defn agent-ex []
  (def tickets-sold (agent 0))
  (send tickets-sold + 15)
  (println)
  (println "Tickets " @tickets-sold)
  
  (send tickets-sold + 10)
  (await-for 100 tickets-sold)
  (println "Tickets " @tickets-sold)
  (shutdown-agents))

(defn -main [& args]
  (agent-ex)
  )

alexmiller 2020-09-15T13:01:57.230100Z

That's not a message from Clojure itself, must be something from your tooling. If something in your tooling is using agents or futures, shutdown-agents could be breaking the tooling itself possibly

zackteo 2020-09-15T13:13:43.231300Z

Right! Maybe NREPL itself then o:

alexmiller 2020-09-15T13:19:56.231800Z

looks like it

zackteo 2020-09-15T13:36:39.232300Z

Right! Thanks for your help! 🙂

seancorfield 2020-09-15T17:03:41.234700Z

@zackteo def should only be used at the top-level. If you want a local binding, use let instead.

seancorfield 2020-09-15T17:04:23.234900Z

And, yeah, most REPL tooling will break if you call (shutdown-agents) since it will kill off stuff inside the tooling, not just your program.

seancorfield 2020-09-15T17:05:45.235100Z

The safer idea is to move the (shutdown-agents) call into -main so it's

(defn -main [& args]
  (agent-ex)
  (shutdown-agents))
that makes your agent-ex function safe to call from the REPL.

Matthew Pettis 2020-09-15T19:47:40.237200Z

I'm trying to use Double/isNaN as a predicate in some, but clojure interprets it as a static field. Is this the idiomatic way of dealing with such things?

Matthew Pettis 2020-09-15T19:47:47.237400Z

rules-engine.thrashing02=> (Double/isNaN ##NaN)
true

rules-engine.thrashing02=> (some Double/isNaN [##NaN 2])
Syntax error compiling at (form-init5367077877367404867.clj:1:1).
Unable to find static field: isNaN in class java.lang.Double

rules-engine.thrashing02=> (some #(Double/isNaN %) [##NaN 2])
true

dpsutton 2020-09-15T19:48:34.237900Z

#(Double/isNan %) is the idiomatic way

👍 1
dpsutton 2020-09-15T19:49:10.238400Z

i believe the reason is that methods are not first class objects on the JVM.

✔️ 1
teodorlu 2020-09-16T07:05:22.275500Z

I really appreciate the built-in predicates for string?, vector?, etc, rather than having to go through another function, like Python's isinstance("sheep", str), especially in combination with other functions like some? or map.

Matthew Pettis 2020-09-15T19:50:22.238500Z

Ok, thanks. Only quirk I have is that this is already in an anonymous function, which I cannot nest, so I have to do something else. But this makes sense, thank you.

dpsutton 2020-09-15T19:51:06.238700Z

make either the outer or the inner a (fn [thing] ...) version. probably the outer since #(Double/isNan %) is quite simple

Matthew Pettis 2020-09-15T19:51:32.238900Z

yep, that's what I did:

👍 1
Matthew Pettis 2020-09-15T19:51:39.239100Z

(fn [it] (Double/isNaN it))

Matthew Pettis 2020-09-15T19:52:20.239400Z

and yep, it's simple, so the fn construct works well.

Nazar Trut 2020-09-15T20:28:56.240Z

Hey

alexmiller 2020-09-15T20:31:14.240100Z

it might be worth making a nan? function in core

➕ 3
Nazar Trut 2020-09-15T21:11:40.240900Z

(def first-test '(not x))
(def second-test '(not (not a)))
(def third-test '(not (not (and a b))))
(def fourth-test '(not (not (not (not c)))))
(defn not-elmination
  [expression]
  (cond
    (= first-test expression) set '{}
    (= second-test expression) set '{a}
    (= third-test expression) set '{(and a b)}
    (= fourth-test expression) set '{(not (not c))}

    :else set '{})

  )
I am trying to return sets here and im getting this error "Map literal must contain an even number of forms"

Nazar Trut 2020-09-15T21:11:49.241100Z

Anyone know why?

bronsa 2020-09-15T21:16:10.241400Z

#{} is the set literal, {} is the map literal

bronsa 2020-09-15T21:16:49.241700Z

set there makes no sense, what are you trying to do

Nazar Trut 2020-09-15T21:17:40.242500Z

So if the user enters '(not x), the cond checks and i want to return a set {(not x)}

bronsa 2020-09-15T21:18:09.243100Z

ok, set '{(not x)} makes no sense, you want #{'(not x)}

bronsa 2020-09-15T21:18:59.243800Z

'#{(not x)} also works, it'll quote the entire set instead of the specific element but in this case they're the same thing

Nazar Trut 2020-09-15T21:19:46.244200Z

So i should be returning #'{a} for example

bronsa 2020-09-15T21:19:55.244400Z

no

bronsa 2020-09-15T21:20:00.244600Z

#{'a}

bronsa 2020-09-15T21:20:07.244800Z

or '#{a}

bronsa 2020-09-15T21:20:32.245500Z

those are equivalent to #{(quote a)} and (quote #{a}) respectively

bronsa 2020-09-15T21:20:48.245900Z

#'{ is not valid syntax

bronsa 2020-09-15T21:21:09.246100Z

the syntax for a set is #{ elements.. }

bronsa 2020-09-15T21:21:49.246900Z

so you can use ' either in front of the whole set to quote all the values in the set, or in front of an element in the set

Nazar Trut 2020-09-15T21:21:58.247200Z

Ok thanks for the information, I will apply that to my code rn

Nazar Trut 2020-09-15T21:24:47.248700Z

hmm now im getting "cond requires an even number of forms"

Nazar Trut 2020-09-15T21:24:52.249Z

(def second-test '(not (not a)))
(def third-test '(not (not (and a b))))
(def fourth-test '(not (not (not (not c)))))
(defn not-elmination
  [expression]
  (cond
    (= second-test expression)  #{'a}
    (= third-test expression) #{'(and a b)}
    (= fourth-test expression) #{'(not (not c))}
    :else set '#{})

  )

seancorfield 2020-09-15T21:24:56.249300Z

@ntrut Parentheses are important in Clojure and mean "function call" (in general) so set '{} is two unrelated expressions but (set '{}) would be a function call -- and would call set on '{}

bronsa 2020-09-15T21:25:04.249500Z

:else set '#{}is wrong

seancorfield 2020-09-15T21:26:22.250100Z

(because it is three separate expressions)

Nazar Trut 2020-09-15T21:26:41.250500Z

Sorry, its hard getting used to clojure, just recently starting learning it in one of my classes

seancorfield 2020-09-15T21:27:05.250900Z

No worries. Lisps are always a bit tricky to get used to.

seancorfield 2020-09-15T21:27:18.251200Z

And it also depends what languages you're used to.

seancorfield 2020-09-15T21:27:57.252Z

In Java or similar: obj.method(arg) In Clojure (method obj arg)

Nazar Trut 2020-09-15T21:28:21.252600Z

Defiantly not used to this, I code in Java/C/Pyhon

Nazar Trut 2020-09-15T21:28:34.252900Z

New territory for me here

3
Nazar Trut 2020-09-15T21:29:59.253400Z

What is wrong with my else?

dpsutton 2020-09-15T21:31:31.254200Z

:else expresssion but you have :else a-function an-empty-set

dpsutton 2020-09-15T21:33:35.255Z

(also, you can just use #{} as an empty set. there are no forms in it that you are quoting)

Nazar Trut 2020-09-15T21:49:46.255400Z

Yep using #{} fixed my else problem and now my program works

Nazar Trut 2020-09-15T21:49:48.255600Z

thanks people

Mitch 2020-09-15T22:24:41.259500Z

Regarding the infer-externs cljs compiler option: Are you intended to move the generated externs in inferred_externs.js to a file that you use for advanced compile? Or is it supposed to be enough to use type hinting and infer-externs when you use advanced compile?

seancorfield 2020-09-15T22:34:25.260800Z

@mitchell_clojure If no one answers here, you might try #clojurescript -- I'm not sure how many active folks here are familiar with ClojureScript compilation issues.

Mitch 2020-09-15T22:40:00.262400Z

Thanks Sean! Hard to know sometimes if I am missing something simple sometimes

Joel 2020-09-15T23:11:30.263600Z

if i have an defrecord instance, is there a simple way to create a new one with just one value substituted, sort of like update-in does for maps?

alexmiller 2020-09-15T23:11:57.263800Z

you can use update

💯 1
alexmiller 2020-09-15T23:12:08.264Z

records are maps

alexmiller 2020-09-15T23:12:31.264200Z

or assoc into it

alexmiller 2020-09-15T23:13:44.264500Z

user=> (defrecord Point [x y])
user.Point
user=> (def r (->Point 1 2))
#'user/r
user=> (update r :x inc)
#user.Point{:x 2, :y 2}
user=> (assoc r :x 10)
#user.Point{:x 10, :y 2}