Fork me on GitHub

that's super generic. normally functions are more like (defn process-orders [orders] (loop [order orders] .... where it works on some conceptual object like orders, options, some kind of plural if you are looping. And I use the singular plucked from the pluralized collection


if the arg changes on each loop but remains conceptually the same thing, it’s a state accumulator and that could be reflected in the name


(also in all cases where arg is a collection being consumed starting in the beginning and in order, the loop should be replaced with a reduce, even if you need to short-circuit (see reduced))


this is 1) for readability 2) for performance


((fn foo [x] (when (> x 0) (conj (foo (dec x)) x))) 5)
^ from 4clojure (simple recursion) not sure how the result gets put into a it an effect of the when? obviously you can't do (conj (conj 5) 4) ...


@imdaveho when returns nil if the condition is false, and if you conj to nil you get a list


it’s an eager list comprehension


(using non-tail recursion)


oh waaat...ok that explains it...just had to know that property; i guess that makes it "monoidal" <- i think


one way to rewrite it

user=> (-> (conj nil)
           (conj 1)
           (conj 2)
           (conj 3)
           (conj 4)
           (conj 5))
(5 4 3 2 1)


@imdaveho also, that rewrite should make it obvious how to rewrite (perhaps as a loop or iterate) in order to reduce stack usage and improve code clarity


@noisesmith yeah that makes it suuuuper was the whole conj nil => () that threw me


@noisesmith you said loop should be replaced with reduce for performance. Is this small example study outdated then? ..It is 6 years old lol. But has reduce's performance improved since this was written? --


yes - reduce is optimized for many inputs and it’s relatively recent


I see. Cool, thx


also it would be faster if they used * instead of #(* %1 %2)


@byron-woodfork I’m running benchmarks on those with 1.8 to double check what I’m claiming here


using criterium so each benchmark takes a little while


I suppose I probably could've/should've done the same too before asking lol


well, I made the claim, so I should provide proof 😄


haha fair enough


@byron-woodfork as of 1.8, slow-factorial is the fastest one on that page

user=> (defn fast-factorial [number]
  #_=>   (loop [n number factorial 1]
  #_=>     (if (zero? n)
  #_=>       factorial
  #_=>       (recur (- n 1) (* factorial n)))))
user=> (defn fast-no-loop-factorial
  #_=>   ([number] (fast-no-loop-factorial number 1))
  #_=>   ([number factorial]
  #_=>    (if (zero? number)
  #_=>      factorial
  #_=>      (recur (- number 1) (* factorial number)))))
user=> (defn recursive-factorial
  #_=>   ([number] (recursive-factorial number 1))
  #_=>   ([number factorial]
  #_=>    (if (zero? number)
  #_=>      factorial
  #_=>      (recursive-factorial (- number 1) (* factorial number)))))
user=> (defn slow-factorial [number]
  #_=>    (reduce * (range 1 (inc number))))
user=> (crit/bench (fast-factorial 20))
WARNING: Final GC required 7.723499618768098 % of runtime
Evaluation count : 104901060 in 60 samples of 1748351 calls.
             Execution time mean : 594.096212 ns
    Execution time std-deviation : 20.328473 ns
   Execution time lower quantile : 559.761955 ns ( 2.5%)
   Execution time upper quantile : 630.269040 ns (97.5%)
                   Overhead used : 2.325356 ns
user=> (crit/bench (fast-no-loop-factorial 20))
Evaluation count : 81981660 in 60 samples of 1366361 calls.
             Execution time mean : 734.338550 ns
    Execution time std-deviation : 26.108761 ns
   Execution time lower quantile : 699.180100 ns ( 2.5%)
   Execution time upper quantile : 782.081357 ns (97.5%)
                   Overhead used : 2.325356 ns
user=> (crit/bench (recursive-factorial 20))
Evaluation count : 76431000 in 60 samples of 1273850 calls.
             Execution time mean : 825.640696 ns
    Execution time std-deviation : 27.265113 ns
   Execution time lower quantile : 773.731849 ns ( 2.5%)
   Execution time upper quantile : 867.129354 ns (97.5%)
                   Overhead used : 2.325356 ns
user=> (crit/bench (slow-factorial 20))
Evaluation count : 138950460 in 60 samples of 2315841 calls.
             Execution time mean : 436.251334 ns
    Execution time std-deviation : 8.966446 ns
   Execution time lower quantile : 425.687944 ns ( 2.5%)
   Execution time upper quantile : 456.996317 ns (97.5%)
                   Overhead used : 2.325356 ns

Found 2 outliers in 60 samples (3.3333 %)
        low-severe       2 (3.3333 %)
 Variance from outliers : 9.3835 % Variance is slightly inflated by outliers


it’s also the smallest and easiest to read version


factorial is challenging because it's a common bench that languages use to measure each other, but it's not emblematic of real world code


yeah I would be fascinated to see an example of a benchmark of something that closer matches real world code patterns


it's like for concurrency frameworks the standard thing to benchmark is a ring of processes passing a message like the telephone game... it's somewhat useful, but not emblematic of real code in the wild


I’m using queues in immutants messaging module.


I don't know how active it is, but there's an #immutant channel if no one answers you here.


Thanks, but I have no problems with the module, I can receive messages just fine. I just can’t find a way to “run a job on a background thread under supervision and restart it if it errors, forever” in clojure.


Now I want to consume messages from that queue, one at a time in another thread.


Restarting said thread if it errors.


How would I accomplish that in clojure?


@juanjo.lenero Why do you have to use threads? I would advise using channels instead.


Is that channel from core.async?


(defn do-work []
  (if-let [msg (receive my-queue)]
    (some-func-that-may-throw msg)
      (Thread/sleep 1000)

(.start (Thread. do-work))


@rinaldi I’ll look into channels, just wondered if there was a way to do it without them.


That’s where I’m at, it works, but if that thread throws I don’t know how to restart it.


@juanjo.lenero The reason why I advise channels instead is that even though the thread API is simple in Clojure, it can be quite expensive to spawn up new ones, not to mention orchestrating things with them. Your use case sounds perfect for channels. They're pretty much lightweight threads, so you can spawn up many of them without paying too much.


Imagine a channel with incoming messages where you send things to execute concurrently then another one with the responses, that you pass to each task you execute. You can keep sending stuff to the responses channel and create a loop to analyze whatever comes in there. If you got an error back it's fine, just send it back to the incoming channel. Since channels accept whatever data structure, you can use good old hash maps. Think like:

{:success false
:attempt 2
:message "Something bad"}


Let me know if it's clear. I myself am new to this but have been using channels a lot lately.


I’m actually restricted by a third-party API that only allows 1 request/sec so I don’t need much concurrency, I just don’t want to block the main thread.


Gotcha, this changes things a bit


You can then simply use future and deref no?


(defn parent []
  (let [response (future (whatever-task))]
    (when realized? response
      (if @response nil ;; Assuming you return `nil` for errors
                    (println "It worked")))))


Yea, I think something like that can work, thank you


in regards to the more general question of "how to do something repeatedly and retry if it fails, the general template looks like

(while (should-continue)
  (try (do-something)
     (catch Exception _)))
- as long as the error handling block is inside the looping construct it won't interrupt the processing of more input