Fork me on GitHub

Why is the former faster than the latter?

(require '[criterium.core :as c])
  (def data (vec (range 100)))

    (into [] (comp (filter even?) (map inc)) data) {}))

  ;; Evaluation count : 54365280 in 60 samples of 906088 calls.
  ;;              Execution time mean : 1.112239 µs
  ;;     Execution time std-deviation : 10.197468 ns
  ;;    Execution time lower quantile : 1.099641 µs ( 2.5%)
  ;;    Execution time upper quantile : 1.144325 µs (97.5%)
  ;;                    Overhead used : 2.033354 ns
  ;; Found 5 outliers in 60 samples (8.3333 %)
  ;; 	low-severe	 1 (1.6667 %)
  ;; 	low-mild	 1 (1.6667 %)
  ;; 	high-mild	 3 (5.0000 %)
  ;;  Variance from outliers : 1.6389 % Variance is slightly inflated by outliers 

    (into [] (comp (map inc) (filter even?)) data) {}))
  ;; Evaluation count : 39379800 in 60 samples of 656330 calls.
  ;;              Execution time mean : 1.537396 µs
  ;;     Execution time std-deviation : 12.310657 ns
  ;;    Execution time lower quantile : 1.515765 µs ( 2.5%)
  ;;    Execution time upper quantile : 1.568869 µs (97.5%)
  ;;                    Overhead used : 2.033354 ns  ;; 

  ;; Found 3 outliers in 60 samples (5.0000 %) 
  ;;  low-severe	 3 (5.0000 %)
  ;;  Variance from outliers : 1.6389 % Variance is slightly inflated by outliers


Because you invoke inc half as many times there.


In the first one, you only increment the even ones. In the second one you increment everything and then filter out the even ones. The two shouldn't even have the same result by the way, so it's not equivalent logic. If you increment first, you make the odds even and then remove the evens, leaving you with odds. On the other you remove the evens and increment the odds, leaving you with only evens.

(into []
  (map inc)
  (filter even?))
 [1 2 3 4])
> [2 4]
(into []
  (filter even?)
  (map inc))
 [1 2 3 4])
> [3 5]


@U0K064KQV @U2FRKM4TW I think I’m misunderstanding something. The doc for comp says it applies to the right most function when passes the result to the left. If that’s true, the first example will inc first then filter, the second will filter then inc. if that’s true then the second example will inc half the number of the first, so it should be faster, no?


In this case, you're comping not inc and even? but rather (map inc) and (filter even?), which are transducers. It might not be obvious at the first glance why it's so, but it becomes rather clear when you learn more about transducers. Here's a documentation section on comp with transducers specifically:

🎯 1
💯 1

Oh! Thanks!

👍 1

Ya, when using comp for transducers, the order becomes left to right. Comp does compose right to left, but with transducer it ends up applying them left or right.


A little confusing I know.


can i rely on this for "normal" clojure maps regardless of size?

(= (zipmap (keys m) (vals m)) m)



👍 1

Consistent ordering is promised by the docs of keys and vals:

Al Z. Heymer10:01:36

Why is it that if you create an ex-info with a nil ex-data field, an IllegalArgumentException is thrown? I can use nil (nearly) everywhere else, but an ex-data-map is not valid with nil ?

Al Z. Heymer11:01:42

Additionally, the doc-string tells us nothing about this behaviour, which I find quite puzzling.


> that carries a map of additional data. nil is not a map

Al Z. Heymer11:01:26

Ok, but (ex-data (Exception. "foo")) is nil . Subsequently, you would need to introduce an instance check for ExceptionInfo or an (or (ex-data e) {}) "catch". I think that is bad reasoning, tbh. If you provide nil data, the intention is clear: No additional data can be provided. An IllegalArgumentException makes no sense to me.


Not a lengthy explanation but an explanation, in the original ticket:

👀 1
Al Z. Heymer12:01:38

I understand that there was an uncertainty to whether nil should be a valid argument, yet this wasn't picked up and discussed, because of the initial doc-string, which still does not explicitly tell you that it throws an IllegalArgumentException in that rather possible "intuitively correct" case. I do understand the reasoning to why ex-data should return nil, but it isn't really coherent with the behaviour of non-existent exception data in java exceptions and an illegal argument of ex-info

Alex Miller (Clojure team)13:01:10

There is a newer ticket about this (can’t easily search on my phone to find it) and I am hoping to fix it in 1.12. I agree with you that this is not good (and it’s entirely too easy to write error handling code that fails at runtime when there’s an error).


Seems like this is the one: > ex-info should be tolerant of nil data param

🙌 1

What about adding a single-arity variant of ex-info that would simply use empty map (or nil) as the second argument?


That would still make things like

(let [data (something-that-can-return-nil)]
  (throw (ex-info "I might never be thrown" data)))


Oh yeah; I was just wondering some many times why ex-info doesn't accept a single, msg, argument 🙂

☝️ 1
Alex Miller (Clojure team)15:01:15

there's more work on this elsewhere that isn't visible here

Alex Miller (Clojure team)15:01:40

I'm working on several ex-info things together

👍 2

I'm looking for something that acts as a promise, but lets me call a function on deref/`realized?` (to see if done yet). Does this exist or should I just go ahead and write it myself?


(realized? a-promise) works right? if i understand your requirement correctly.

Al Z. Heymer11:01:47

I think this could work with a one-element lazy-seq . Once you consume it, the task is spawned


@U02PS2BNULE hmm no, I want to perform the check each time it's checked/deref:ed until "done"


@U7ERLH6JX hmm no, I want something like

(def p (promise (fn [] (println "checking...") (are-we-done-yet?)))

(realized? p) ;; prints "checking..."


basically, I have an interface that takes a promise, but in this particular case I don't have a thread I can use to deliver the promise, so it'd be convenient to 'deliver' the promize lazily instead


what's the use case youre trying to model? maybe that helps in understanding more?


sounds like a delay


but it won't call the body when checked for realized?


There's a library function (defn do-something [a-promise]) that expects a promise. I have a function are-we-done-yet?, that I can call to check if the promise should be delivered. I want to connect these two without having to create a thread that repeatedly calls are-we-done-yet? just so it can deliver the promise.


yeah delay doesn't quite do the trick


so essentially like a callback when the promise is delivered?


no, like a callback that the promise uses to check if it should act delivered


hmm, not sure if there's something off the shelf for this


yeah alright, guess I'll write it myself


why do you need to call are-we-done-yet? repeatedly? That could be a single deliver call to unlock other threads which are waiting for the promise.


@U04V4KLKC there's really only one thread here, and it's managed by do-something


since are-we-done-yet? is a (non-blocking) function and not a promise, I'd need to loop to see if it's done


is it clojurescript?


essentially what I'm trying to avoid is

(let [*p (promise)]
    (while-not (deref *p 0 false)
      (when (are-we-done-yet?)
        (deliver *p true))))
  (do-something *p))


no, regular old Clojure


but it's fine, I'll just write the thing I need


so you have a library that expects a promise provided by your code and you want to realize it only when its needed by the library code?


without doing any unnecessary work


that is how delay works


no, not quite


with delay, I'd still need the loop


and there'd still be a thread


> with delay, I'd still need the loop why? because when you can create a delay you don't have a value for it?


delays are only computed once, and are-we-done-yet? isn't blocking


so I'd have to do something like

(delay (while (not (are-we-done-yet)))) true)


also, realized? works differently on delays than on promises


hm... differently how?


well, I could be wrong, but afaik realized? doesn't trigger the computation

Matthew Downey15:01:24

Does a more fully-featured promise abstraction solve this?

Jo Øivind Gjernes15:01:58

How can i close an “infite” io.reader ? (.close reader) seems to block :thinking_face:


Do you have some more code to look at?


What does read-from do?

Jo Øivind Gjernes15:01:27

Parsing json in my case

Jo Øivind Gjernes15:01:33

But i don’t think that matters :thinking_face:

Jo Øivind Gjernes15:01:45

It’s basically having an infinite source of messages


And when do you want to break out?


doseq docs:

Repeatedly executes body (presumably for side-effects) with
bindings and filtering as provided by "for".  Does not retain
the head of the sequence. Returns nil.


(let [close? (promise)]
  (with-open [rdr (create-a-reader)]
    (loop []
      (when-not (.deref close? 1 false)
        (let [msg (read-from rdr)]
          (process-msg msg)
    (close [_]
      (deliver close? true))))


with-open will take care about closing the reader


I often use line-seq and it does all right on many occasions


but it depends on what you want to do 🙂


Charred returns an auto-closeable json supplier for these types of purposes -

Ben Lieberman18:01:57

is clojure.core/time's printed value analogous to the real time displayed by the time utility or something else?


(source time) shows it is just Java's System/nanoTime


(and purely an elapsed value from that)

Ben Lieberman18:01:53

I am forever forgetting about source :face_palm::skin-tone-2: thanks Sean


Is there a macro-less obvious way to implement a call to for with a variable number of arguments that I missed or is this an acceptable case for writing a macro?

(defmacro cartesian [& args]
  (let [sym-arg-pairs (map #(list (gensym) %) args)
        syms (mapv first sym-arg-pairs)]
    `(for ~(into [] (reduce concat sym-arg-pairs))

  (= (for [a (range 3)
           b (range 3)
           c (range 3)
           d (range 3)
           e (range 3)]
       [a b c d e])
     (cartesian (range 3)
                (range 3)
                (range 3)
                (range 3)
                (range 3)))


there is no reason it can't be a function


here's a function version of cartesian product

(defn cart [colls]
  (if (empty? colls)
    (for [more (cart (rest colls))
          x (first colls)]
      (cons x more))))


Oh yeah that is obvious XD Thanks Maybe it's time to go to sleep


For cartesian product (if that is what you are really after, and it wasn’t just a placeholder example) there is also


What's the thread pool size for futures again? Something like logical cores * 2 + 1 or something?


If the size of the thread pool is important to your use case, you should create your own thread pool.


clojure, the language runtime, has 2 threadpools built in, one is unbounded and the other is Runtime.getRuntime().availableProcessors() + 2


core.async has its own threadpools, one is unbounded and the other is maybe capped at 8 (I forget) but there is a system property you can use to change it


the java has also gained a "default" threadpool since clojure was released, and a lot of newer stuff uses that by default if you don't specify a pool to run on (completionstage stuff, the new httpclient)


the thread pool clojure.core/future uses is the unbounded clojure runtime one


thanks hiredman!


enable preview and set them all to v-threads. f* da police


Afaik, Virtual threads are mostly for io bound tadks, not cpu bounded computations