Fork me on GitHub
#clojure
<
2024-04-12
>
craftybones05:04:42

Why is there an anonymous fn within the lazy-seq instead of a simple loop binding? This is from the implementation of clojure.core/distinct btw.

([coll]
   (let [step (fn step [xs seen]
                (lazy-seq
                  ((fn [[f :as xs] seen]
                     (when-let [s (seq xs)]
                       (if (contains? seen f)
                         (recur (rest s) seen)
                         (cons f (step (rest s) (conj seen f))))))
                   xs seen)))]
     (step coll #{}))

phronmophobic05:04:34

Presumably, it's to act as the recur target if the value has already been seen.

craftybones06:04:13

I mean, why not a loop?

craftybones06:04:41

This is a neat hack. I’m going to steal it

craftybones06:04:56

I always wrote a loop in a condition instead of doing it this way. So elegant

phronmophobic06:04:19

To be honest, I'm not sure why it doesn't use a loop instead. A looping version might look like:

(let [step (fn step [xs seen]
             (lazy-seq
              (loop [xs (seq xs)
                     seen seen]
                (if xs
                  (let [x (first xs)]
                    (if (contains? seen x)
                      (recur (next xs) seen)
                      (cons x
                            (step (next xs) (conj seen x)))))))))]
  (step coll #{}))
This seems to behave similarly, but there might be some subtle differences in how lazy the result is. Since this was added in 1.0, it's also possible that it could theoretically be improved, but is not worth changing for theoretical benefits given the risk of breaking existing code in a subtle way. I don't don't know for sure.

craftybones06:04:35

Yes. I wrote it similarly too, which was why my question in the first place

mogverse08:04:43

I guess because the anonymous function has been named step and that name has been used in the call itself? Talking about the name given besides fn and not in the let binding.

roklenarcic14:04:41

Are there any other sequence processing functions besides reduce that can be short circuited with reduced value or similar?

p-himik14:04:28

Transducers, reductions, iteration.

roklenarcic14:04:48

thanks, iteration is interesting and I always forget about it

p-himik14:04:55

Ah, and, of course, anything that uses reduce itself and passes it a function that can return reduced. I see reduce-kv and run!, maybe there are others.

igrishaev15:04:12

Loop/recur is also your good friend

p-himik15:04:43

*but unrelated to reduced, isn't affected by it.

roklenarcic16:04:14

Ended up just manually writing loop recursive

Noah Bogart15:04:09

why does splitv-at in clojure 1.12 return a vector for take and a seq for drop? why not both vectors?

p-himik15:04:31

I don't know the actual reasoning, but doing it this way prevents fully realizing seqs.

👍 1
dpsutton15:04:43

i don’t think it returns a vector for take?

dpsutton15:04:50

(defn split-at
  "Returns a vector of [(take n coll) (drop n coll)]"
  {:added "1.0"
   :static true}
  [n coll]
    [(take n coll) (drop n coll)])

Noah Bogart15:04:00

this is the new 1.12 splitv-at

p-himik15:04:00

split*v*-at.

👍 1
dpsutton15:04:47

allows for infinite seqs i suppose

Noah Bogart15:04:51

yeah that makes sense

Noah Bogart15:04:15

(split-at 10 (range)) works, so should (splitv-at 10 (range))

p-himik15:04:42

Well, "X works so should Y" is by itself not a very good basis. :) map works on inf seqs, mapv doesn't. Well, it does work but it hangs, of course.

p-himik15:04:46

That doesn't really answer the question in the OP of "why not (into [] (drop n) coll)".

Noah Bogart15:04:03

yeah it answers the "why does this exist" and i understand that the goal is efficiently splitting at a specific point, which the IDrop interface provides.

Noah Bogart15:04:27

i guess it's just "implementation details" for why they're different types

ghadi15:04:07

vectors can efficiently drop, but cannot re-form a vector

Alex Miller (Clojure team)16:04:37

it is designed for the case where you are splitting your way through an arbitrary collection (which may be lazy / infinite)

👍 1
Alex Miller (Clojure team)16:04:01

retaining the collection type for the remainder allows you to continue efficiently dropping from it without incurring the cost to copy the data into another collection

👍 1
1
Noah Bogart16:04:45

that makes sense, thank you for the explanation

Alex Miller (Clojure team)16:04:02

partitionv[-all] is one way to do this, but splitv-at is better for the explicit loop/recur variant of that where you have more control over when to stop

Alex Miller (Clojure team)16:04:16

both split-at and splitv-at get the drop advantages, it's just whether you want your partitions as vectors or seqs (vectors matches partitionv - that change is why we introduced a new function there, to avoid breaking existing assumptions)

Noah Bogart16:04:15

that makes sense. the lack of a vector in the drop branch feels incongruent with the other -v functions but it's a small price to pay

Alex Miller (Clojure team)16:04:22

it is a vector in the place where that's important

andrea19:04:50

Is this the intended behavior for when-let? It does something surprising with associative deconstructing:

(defn test-when-let [k] 
 (let [m {:foo "foo"}]
   [(when (m k)
      {:first (m k)})
    (when-let [bar (m k)]
      {:second bar})
    (when-let [{bar k} m]
      {:third bar})]))

;; returns [{:first "foo"} {:second "foo"} {:third "foo"}]
;; all good!
(test-when-let :foo) 

;; returns [nil nil {:third nil}]
;; was expecting [nil nil nil]
(test-when-let :bar) 

👍 1
ghadi19:04:42

when-let doesn't examine the binding for the when- part of when-let

ghadi19:04:56

it only looks at the value on the rhs - and you gave a map

ghadi19:04:08

maps are always truthy

andrea19:04:36

ah that makes sense, counter intuitive at first but more obvious if you deconstruct with more than one binding

👍 1
1
ghadi19:04:49

clojure.core/when-let
([bindings & body])
Macro
  bindings => binding-form test

  When test is true, evaluates body with binding-form bound to the value of test

🙏 1
👍 1
didibus22:04:59

java.lang.ClassNotFoundException: sun.misc.Launcher$ExtClassLoader Anyone knows the cause/fix, when trying to upgrade from Java8 to Java17 using Clojure 1.10.0

p-himik22:04:19

What's the stacktrace?

didibus22:04:16

[java] Exception in thread "main" java.lang.ExceptionInInitializerError [java] at dynapath.defaults__init.load(Unknown Source) [java] at dynapath.defaults__init.<clinit>(Unknown Source) [java] at java.base/java.lang.Class.forName0(Native Method) [java] at java.base/java.lang.Class.forName(Class.java:467) [java] at clojure.lang.RT.classForName(RT.java:2207) [java] at clojure.lang.RT.classForName(RT.java:2216) [java] at clojure.lang.RT.loadClassForName(RT.java:2235) [java] at clojure.lang.RT.load(RT.java:453) [java] at clojure.lang.RT.load(RT.java:428) [java] at clojure.core$load$fn__6824.invoke(core.clj:6126) [java] at clojure.core$load.invokeStatic(core.clj:6125) [java] at clojure.core$load.doInvoke(core.clj:6109) [java] at clojure.lang.RestFn.invoke(RestFn.java:408) [java] at clojure.core$load_one.invokeStatic(core.clj:5908) [java] at clojure.core$load_one.invoke(core.clj:5903) [java] at clojure.core$load_lib$fn__6765.invoke(core.clj:5948) [java] at clojure.core$load_lib.invokeStatic(core.clj:5947) [java] at clojure.core$load_lib.doInvoke(core.clj:5928) [java] at clojure.lang.RestFn.applyTo(RestFn.java:142) [java] at clojure.core$apply.invokeStatic(core.clj:667) [java] at clojure.core$load_libs.invokeStatic(core.clj:5985) [java] at clojure.core$load_libs.doInvoke(core.clj:5969) [java] at clojure.lang.RestFn.applyTo(RestFn.java:137) [java] at clojure.core$apply.invokeStatic(core.clj:667) [java] at clojure.core$require.invokeStatic(core.clj:6007) [java] at clojure.core$require.doInvoke(core.clj:6007) [java] at clojure.lang.RestFn.invoke(RestFn.java:421) [java] at dynapath.util$loading__4920__auto__.invoke(util.clj:1) [java] at dynapath.util__init.load(Unknown Source) [java] at dynapath.util__init.<clinit>(Unknown Source) [java] at java.base/java.lang.Class.forName0(Native Method) [java] at java.base/java.lang.Class.forName(Class.java:467) [java] at clojure.lang.RT.classForName(RT.java:2207) [java] at clojure.lang.RT.classForName(RT.java:2216) [java] at clojure.lang.RT.loadClassForName(RT.java:2235) [java] at clojure.lang.RT.load(RT.java:453) [java] at clojure.lang.RT.load(RT.java:428) [java] at clojure.core$load$fn__6824.invoke(core.clj:6126) [java] at clojure.core$load.invokeStatic(core.clj:6125) [java] at clojure.core$load.doInvoke(core.clj:6109) [java] at clojure.lang.RestFn.invoke(RestFn.java:408) [java] at clojure.core$load_one.invokeStatic(core.clj:5908) [java] at clojure.core$load_one.invoke(core.clj:5903) [java] at clojure.core$load_lib$fn__6765.invoke(core.clj:5948) [java] at clojure.core$load_lib.invokeStatic(core.clj:5947) [java] at clojure.core$load_lib.doInvoke(core.clj:5928) [java] at clojure.lang.RestFn.applyTo(RestFn.java:142) [java] at clojure.core$apply.invokeStatic(core.clj:667) [java] at clojure.core$load_libs.invokeStatic(core.clj:5985) [java] at clojure.core$load_libs.doInvoke(core.clj:5969) [java] at clojure.lang.RestFn.applyTo(RestFn.java:137) [java] at clojure.core$apply.invokeStatic(core.clj:667) [java] at clojure.core$require.invokeStatic(core.clj:6007) [java] at clojure.core$require.doInvoke(core.clj:6007) [java] at clojure.lang.RestFn.invoke(RestFn.java:436) [java] at bultitude.core$loading__5569__auto____3.invoke(core.clj:1) [java] at bultitude.core__init.load(Unknown Source) [java] at bultitude.core__init.<clinit>(Unknown Source) [java] at java.base/java.lang.Class.forName0(Native Method) [java] at java.base/java.lang.Class.forName(Class.java:467) [java] at clojure.lang.RT.classForName(RT.java:2207) [java] at clojure.lang.RT.classForName(RT.java:2216) [java] at clojure.lang.RT.loadClassForName(RT.java:2235) [java] at clojure.lang.RT.load(RT.java:453) [java] at clojure.lang.RT.load(RT.java:428) [java] at clojure.core$load$fn__6824.invoke(core.clj:6126) [java] at clojure.core$load.invokeStatic(core.clj:6125) [java] at clojure.core$load.doInvoke(core.clj:6109) [java] at clojure.lang.RestFn.invoke(RestFn.java:408) [java] at clojure.core$load_one.invokeStatic(core.clj:5908) [java] at clojure.core$load_one.invoke(core.clj:5903) [java] at clojure.core$load_lib$fn__6765.invoke(core.clj:5948) [java] at clojure.core$load_lib.invokeStatic(core.clj:5947) [java] at clojure.core$load_lib.doInvoke(core.clj:5928) [java] at clojure.lang.RestFn.applyTo(RestFn.java:142) [java] at clojure.core$apply.invokeStatic(core.clj:667) [java] at clojure.core$load_libs.invokeStatic(core.clj:5985) [java] at clojure.core$load_libs.doInvoke(core.clj:5969) [java] at clojure.lang.RestFn.applyTo(RestFn.java:137) [java] at clojure.core$apply.invokeStatic(core.clj:667) [java] at clojure.core$require.invokeStatic(core.clj:6007) [java] at clojure.core$require.doInvoke(core.clj:6007) [java] at clojure.lang.RestFn.invoke(RestFn.java:3659) [java] at cloverage.coverage$loading__5569__auto____1.invoke(coverage.clj:1) [java] at cloverage.coverage__init.load(Unknown Source) [java] at cloverage.coverage__init.<clinit>(Unknown Source) [java] at java.base/java.lang.Class.forName0(Native Method) [java] at java.base/java.lang.Class.forName(Class.java:467) [java] at clojure.lang.RT.classForName(RT.java:2207) [java] at clojure.lang.RT.classForName(RT.java:2216) [java] at clojure.lang.RT.loadClassForName(RT.java:2235) [java] at clojure.lang.RT.load(RT.java:453) [java] at clojure.lang.RT.load(RT.java:428) [java] at clojure.core$load$fn__6824.invoke(core.clj:6126) [java] at clojure.core$load.invokeStatic(core.clj:6125) [java] at clojure.core$load.doInvoke(core.clj:6109) [java] at clojure.lang.RestFn.invoke(RestFn.java:408) [java] at clojure.lang.Var.invoke(Var.java:384) [java] at clojure.lang.Util.loadWithClass(Util.java:250) [java] at cloverage.coverage.<clinit>(Unknown Source) [java] Caused by: java.lang.ClassNotFoundException: sun.misc.Launcher$ExtClassLoader [java] at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) [java] at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) [java] at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:525) [java] at java.base/java.lang.Class.forName0(Native Method) [java] at java.base/java.lang.Class.forName(Class.java:375) [java] at dynapath.defaults$fn__68.<clinit>(defaults.clj:12) [java] ... 100 more

p-himik22:04:24

A tentative guess - an old version of CIDER nREPL?

didibus22:04:29

Hum, I don't think it's in the dependency chain. I am runing cloverage though, and error is coming from that

p-himik22:04:24

The root cause is at [java] at dynapath.defaults$fn__68.<clinit>(defaults.clj:12) and before, which is not shown. dynapath.defaults used to be a vendored namespace in CIDER nREPL in 0.18.

p-himik22:04:51

(Maybe still is, dunno).

p-himik22:04:21

Nah, probably not that. That line seems to only extend classloader types to some protocols.

p-himik22:04:12

Oh, no - that is that. I was looking at a newer version. Here it is: https://github.com/tobias/dynapath/blob/0.2.5/src/dynapath/defaults.clj#L40

p-himik22:04:40

There was a check for a wrong class in 0.2.4. How ancient is your tooling? :)

didibus22:04:53

Ancient lol

didibus22:04:43

It's using 0.2.4 of dynapath

didibus22:04:53

Ya, so I guess I need to upgrade

didibus22:04:40

Wait, how do you trace back it was checking the wrong thing in 0.2.4?

didibus22:04:08

Oh, nevermind, I see it

Colin P. Hill22:04:25

Anyone know of a library for diffing sequences a la the diff POSIX command? I.e., no deep diff, and able to detect insertions and deletions rather than just comparing index-by-index.

Colin P. Hill22:04:51

I could literally just shell out to diff if I really have to, but...

p-himik22:04:32

Yeah, when I needed to diff two JSON files printed out for humans, I ended up with shelling out to diff. :) Couldn't find a more reasonable solution.

Colin P. Hill22:04:36

Hmm if that's a gap in the ecosystem... adds to list of yaks to be shaven later

p-himik23:04:13

It might've easily be a gap in my ability to search. Or a gap in the ecosystem w.r.t. my own very particular needs at the time. It could easily be that your needs are different enough, or that enough time has passed since then, that now there's a thing you can use instead of bin/diff.

p-himik23:04:53

I should add that my needs included the best performance available since it was about relatively large JSON objects. For smaller data, maybe even diff2 by LambdaIsland can be enough, despite it being a deep diff.

Colin P. Hill23:04:45

I could make a deep diff work by doing something like stringifying the data, as long as it can detect insertions. Really I just need the right hand side of the diff. I am looking for items on the right side which are unaccounted for on the left side, where the items are not necessarily unique but are sorted.

Colin P. Hill23:04:51

And I'm looking for them by position in the original right hand side list, because I'll be stripping some data away for the comparison which I must then reconstitute. Configurable equality semantics could achieve that too I suppose

p-himik23:04:12

There's also this, maybe helpful, dunno: https://github.com/juji-io/editscript

Colin P. Hill23:04:12

Oh hmmm. I'd passed over that, but yeah that might do it. I'll do some tinkering. Thanks!