Fork me on GitHub
#clojure
<
2019-04-10
>
mg00:04:23

The arrow I've probably heard requested most frequently is a version of cond-> that threads the value through the tests too

mg03:04:43

@seancorfield oh nice, I like that one

seancorfield03:04:07

And heavily production-tested 🙂

seancorfield04:04:30

@aryyya.xyz Have you asked in #emacs ?

seancorfield04:04:48

(It's been too long since I used Emacs to be able to remember, I'm afraid)

aryyya04:04:43

@seancorfield No I have not, will do. How can I discover what channels exist on this workspace? Also, what editor are you using for Clojure if you don’t mind me asking?

seancorfield04:04:27

Depending on the Slack client, there should be a label on the left Channels which you can click to browse all the hundreds of channels...

aryyya04:04:55

@seancorfield Ah ok I just found that, thanks!

seancorfield04:04:02

...but it's a fair rule of thumb to assume "There's a channel for that!" and just use the switcher (control-k) and start typing 🙂

seancorfield04:04:24

(and there's #slack-help if you need to ask more detailed questions about how-to-slack 🙂 )

seancorfield04:04:15

I switched from Emacs to Atom (+ ProtoREPL) after using Emacs for years... and at the end of last year I switched to Atom + Chlorine.

aryyya04:04:27

@seancorfield Ok, I’ve been using VS Code which has decent integration with Clojure and the REPL. Why did you switch? I find Emacs might be so complicated that I’m spending too much time configuring it and not enough time learning.

aryyya04:04:39

@seancorfield I’ll check that out.

seancorfield04:04:41

Yup. That. 🙂

seancorfield04:04:07

I've tried VSCode several times. Calva is coming along nicely. But I just prefer Atom.

seancorfield04:04:43

(I have an Insider build of VS Code which I dip into from time to time)

seancorfield05:04:04

My workflow these days is to fire up Cognitect's REBL with a Socket REPL and then connect Atom/Chlorine to it and run them side-by-side, so I can "inspect" values directly into REBL, and take advantage of tap> for debugging.

parrot 4
aryyya05:04:02

I’ll check these things out. I tried out Atom before. VS Code seems very similar to me. I don’t mind either of them.

seancorfield05:04:53

Part of what I love about Chlorine is that it's written in ClojureScript and I can hack on it live while I'm editing my work projects 🙂

aryyya05:04:37

That’s the beauty of this stack. I’m still a bit of a beginner, haven’t tried ClojureScript yet.

seancorfield05:04:13

We tried cljs at work about four years ago and it was a pretty rough experience back then so we decided to move forward with JS and React.js on our front end for the time being. But things have changed a lot and cljs is much, much more mature these days so we might look at it again for some projects in future.

seancorfield05:04:45

(we've been doing Clojure in production since 2011)

aryyya05:04:59

What specific industry is that?

seancorfield05:04:45

Online dating. World Singles Networks.

seancorfield05:04:27

We have dozens of dating websites, all powered by Clojure on the back end and React.js on the front end.

seancorfield05:04:52

Over 80k lines of Clojure.

👍 4
yuhan05:04:44

Is there a better way of writing this function?

(defn lazy-distinct
  "return a lazy sequence of coll that terminates
  when a repeated value is encountered"
  [coll]
  (->> coll
    (reductions (fn [[seen _] x]
                  (if (contains? seen x)
                    (reduced :end)
                    [(conj seen x) x]))
      [#{} :start])
    ;; drop :start and :end markers
    (drop 1)
    (drop-last 1)
    (map second)))

seancorfield05:04:58

That seems very complicated... What's with the start/end markers? Aren't you just going to return the original sequence up to the first item that you've already seen?

yuhan05:04:27

yeah, seems like I'm missing something very simple here 😅

seancorfield05:04:11

(reduce (fn [[seen coll] x] (if (seen x) (reduced coll) [(conj seen x) (conj coll x)])) [#{} []] coll)

seancorfield05:04:51

It's not lazy but...

yuhan05:04:54

yeah, for my purposes I need it to be lazy

yuhan05:04:58

i.e. the iterator might be infinite but if it does ever cycle back to a repeated value I want it to terminate

seancorfield05:04:57

Hmm... interesting... So you'd have to track a potentially near-infinite set of "seen" values?

yuhan05:04:01

I guess so, but practically I'm only using the first couple of elements off the seq

yuhan05:04:20

so for performance reasons I also don't want the whole thing to be realized

seancorfield05:04:21

In which case, why do you care whether it's lazy or eager?

dmarjenburgh05:04:26

You can use clojure.core’s distinct transducer, except return a reduced value when a repeated value is encountered.

(defn take-unique []
  (fn [rf]
    (let [seen (volatile! #{})]
      (fn
        ([] (rf))
        ([result] (rf result))
        ([result input]
         (if (contains? @seen input)
           (rf (reduced input))
           (do (vswap! seen conj input)
               (rf result input))))))))

(sequence (take-unique) coll)

seancorfield05:04:58

Or this lazy-seq-based version with a helper:

user=> (defn ld [coll seen] (cond (empty? coll) [] (seen (first coll)) [] :else (lazy-seq (cons (first coll) (ld (rest coll) (conj seen (first coll)))))))
#'user/ld
user=> (defn lazy-distinct [coll] (ld coll #{}))
#'user/lazy-distinct

yuhan06:04:16

Thanks for both those approaches! I learnt a bit more about transducers from this 🙂

seancorfield06:04:42

sequence of a transducing function is a good approach (but a bit more code that a simple lazy seq).

seancorfield06:04:04

One day all processes will be made that way (transducers) 🙂

yuhan06:04:03

I'm still finding it hard to gain intuition about what transducers are "doing" under the hood

yuhan06:04:34

or rather what mental model to use when thinking about them - even though I can follow the logic it's still seems like this magical passing around of higher-order-functions

yuhan06:04:45

comp of transducers behaving in a "backwards" manner being one of the more confusing things

seancorfield06:04:25

Yeah, they definitely take some getting used to. Especially since it's very hard to exercise all three arities.

Ben Hammond09:04:02

I can't shake off the feeling that its confusing design to use different arities for different lifecycle moments

Ben Hammond09:04:26

I understand that its the most efficient way to work with the jvm and is thefore a good pragmatic choice

Ben Hammond09:04:17

but transducers would be easier-to-grasp if they were defined using a record (defrecord Transducer [init-val iterate-fn wrapup-fn]) IMHO

jaihindhreddy08:04:10

@qythium I found it useful to watch the Transducers talk Rich gave at Strange Loop a couple of times. I also found it helpful to understand IReduceInit first, then move on to transducers.

Vikram09:04:34

can anyone tell me how to learn clojure openscad. Thanks

Drake Nelson10:04:20

Hi there! I solving "Sum of the first nth term of Series" from http://codewars.com on clojure, and when I write solution I recived like "time out execution error". I solve the same problem on haskell and take execute time, it's about 0.166972266s on 25000 value of nth series. This is a clojure and haskell code that does the same thing.

clojure
(defn series-sum [n]
  (->> (map #(/ 1 %) (range 1 (java.lang.Integer/MAX_VALUE) 3))
    (take n)
    (reduce +)
    double
    (format "%.2f")))
haskell
import Text.Printf

series :: [Double]
series = map (1/) [1, 4 ..]

seriesSum :: Integer -> String
seriesSum n = printf "%.2f" $ sum $ take (fromInteger n) series
Can anyone tell me, who I can solve this problem on clojure faster? Thank you in advance.

☑️ 4
Drake Nelson10:04:44

As example on clojure with 3000 of nth series execute time is 7971.811645 msecs versus 0.002140344s on haskell

yuhan10:04:52

does it help if you change it to #(/ 1.0 %)?

👍 8
yuhan10:04:21

currently it's doing rational arithmetic instead of floating-point

oyakushev10:04:00

I can confirm that it's 3ms-ish this way.

Drake Nelson10:04:16

Yes. It helped. I guess I do not know something. 😯

yuhan10:04:41

I'd be interested how Haskell compares with its Rational implementation

oyakushev11:04:24

Rational arithmetic being the default for division in Clojure has bitten a lot of people, you are not the first one, unfortunately.

cheatex11:04:46

Just curious. Did the answers match?

mpenet12:04:05

(defn series-sum
  [n]
  (->> (range 1 (java.lang.Integer/MAX_VALUE) 3)
       (transduce (comp (map #(/ 1.0 %)) (take n))
                  +
                  0.0)
       (format "%.2f")))
^ should be slightly faster

mpenet12:04:40

after warmup it clocks at 1.2 ms on my laptop for 25k vs 3.6 for the slightly modified version of yours

ivana12:04:50

now it is time to test loop variant 🙂

oyakushev12:04:00

If run enough times, first solution is 0.4ms vs 0.1ms for @U050SC7SV solution.

mpenet12:04:39

unrolling the whole mess in loop for sure is faster, but too much stuff to type 😛

mpenet12:04:56

transduce version is close enough to the haskell version in terms of syntax

ivana12:04:56

of course. but how said one haskeller - better way do make an unefficient programs is to write code on lazy languages as on the strict ones and do the opposit way 🙂

ivana12:04:35

anyway naive recursion in haskell in this task will be slower an take a HUGE amount of ram )

mpenet12:04:58

won't ghc optimize away the lazy parts since the return value will force computation?

ivana12:04:54

it depends on compiler O option )

ivana12:04:05

by default - not )

mpenet12:04:05

I thought this is where its type system could help with 🙂

ivana12:04:34

but you can always add strict evaluation thru $! or any else, but it is needed to remember )

oyakushev12:04:07

For those interested, this compiles pretty much to identical Java code, and takes ~20us.

(defn series-sum [n]
  (loop [i 1, j 1, acc 0.0]
    (if (< i n)
      (recur (unchecked-inc-int i) (unchecked-add-int j 3) (unchecked-add acc (/ 1.0 j)))
      (format "%.2f" acc))))

mpenet12:04:32

yeah, I tend to prefer explicitness in these cases. Too much magic kills the magic

mpenet12:04:41

20ms? that's way too much no? error in unit?

oyakushev12:04:14

(decompile
(defn series-sum [n]
  (loop [i 1, j 1, acc 0.0]
    (if (< i n)
      (recur (unchecked-inc i) (unchecked-add j 3) (unchecked-add acc (/ 1.0 j)))
      (format "%.2f" acc)))))
public static Object invokeStatic(final Object n) {
        long i = 1L;
        long j = 1L;
        double acc = 0.0;
        while ((i, n)) {
            final long n2 = i + 1L;
            final long n3 = j + 3L;
            acc += Numbers.divide(1.0, j);
            j = n3;
            i = n2;
        }
        return ((IFn)const__8.getRawRoot()).invoke((Object)"%.2f", (Object)acc);
    }

oyakushev12:04:18

Yes, sorry, 20us

ivana12:04:37

loop wins as usual 😁

ivana12:04:22

and about haskell you can uncomment and compare time and memory peak 😉 https://rextester.com/KONQV13764

flefik14:04:59

The API Docs link at the bottom of of spec-alpha2 README is broken. Is this a known bug? https://github.com/clojure/spec-alpha2

Alex Miller (Clojure team)15:04:48

yeah, I've never got around to building it

Alex Miller (Clojure team)15:04:58

typically it builds off releases and we haven't done one

Alex Miller (Clojure team)15:04:38

@cfeckardt I generated it, waiting on github to publish, but should eventually show up at https://clojure.github.io/spec-alpha2/

rplevy19:04:00

Looks like archives are down, showing a Null Pointer Exception

Lennart Buit19:04:02

@plexus you are maintaining that right?

Lennart Buit20:04:45

He is not in this channel, but I think through the magic of ambigous slack options I have maybe summoned him :’). Some parts of clojurians slack log appears to be down.

👍 4
seancorfield20:04:35

This was raised in #slack-help and I tagged him there as well.

borkdude21:04:49

when do I need requiring-resolve? I am loading some namespaces lazily (for performance, I don’t need them every instance of the program).

Alex Miller (Clojure team)21:04:15

are there likely to be multiple threads doing same ns at the same time?

noisesmith21:04:15

I thought it was a convenience for the lazy-loaded require/resolve pattern

noisesmith21:04:15

nice - but now I see the thread safety thing too, which is nice

Alex Miller (Clojure team)21:04:20

it has the added benefit of using the (private) serialized-require

Alex Miller (Clojure team)21:04:44

which is a temporary crutch until we can better fix require

Alex Miller (Clojure team)21:04:35

eventually, serialized-require should just revert to require

borkdude21:04:32

multiple threads? at the moment, no

borkdude21:04:16

I’m hitting the locking issue with GraalVM again when using requiring-resolve, which can be patched, but if I don’t have to 🙂

borkdude21:04:10

hmm, I think this lazy require approach isn’t working at all with GraalVM since it needs eval basically right. so I’ll scratch that idea

Alex Miller (Clojure team)21:04:24

yeah, I was wondering how that worked :)

noisesmith21:04:07

can you use the var method via interop?

ghadi22:04:49

You're gonna want to make sure Graal sees the classes so that it can compile them all

ghadi22:04:57

by preloading them

noisesmith22:04:54

ahh right, which means the whole "last minute lazy require" thing is pointless anyway, I think