Fork me on GitHub
#clojure-dev
<
2022-07-08
>
dominicm03:07:39

I think I've found a bug in clojure 1.11. No minimal repro yet, but this is a repro, it worked under 1.10:

(def flatten
  "A transducer version of clojure.core/flatten"
  (fn [rf]
    (fn
      ([] (rf))
      ([result] (rf result))
      ([result input]
       (if (sequential? input)
         (transduce flatten rf result input)
         (rf result input))))))

(into [] flatten [[1] [2] [3]]) ;; => boom

hiredman03:07:38

What is the exception?

dominicm03:07:49

Execution error (ClassCastException) at user/flatten$fn (REPL:10).
class clojure.lang.PersistentVector cannot be cast to class clojure.lang.ITransientCollection (clojure.lang.PersistentVector and clojure.lang.ITransientCollection are in unnamed module of loader 'app')
Switching impl to this seems to fix it:
(def flatten
  "A transducer version of clojure.core/flatten"
  (fn [rf]
    (fn
      ([] (rf))
      ([result] (rf result))
      ([result input]
       (if (sequential? input)
         (reduce (flatten rf) result input)
         (rf result input))))))

dominicm03:07:12

Probably not correct though, because I'm not using all the arities.

Alex Miller (Clojure team)03:07:30

there were some changes in into completion

Alex Miller (Clojure team)03:07:03

I'm not sure your code is right wrt reduced

Alex Miller (Clojure team)03:07:46

if the inner reduce returns a reduced, you need to wrap in a second reduced (see cat impl)

dominicm03:07:37

You're right, I think that's broken. But I don't think that's the problem in this case.

dominicm03:07:45

into didn't introduce a reduced. I think the problem is that there's now an official completion which calls persistent . And by using rf with transduce we are completing earlier than expected now.

Alex Miller (Clojure team)03:07:04

yeah, I think that makes sense based on the exception

dominicm03:07:19

(def flatten
    "A transducer version of clojure.core/flatten"
    (fn [rf]
      (fn
        ([] (rf))
        ([result] (rf result))
        ([result input]
         (if (sequential? input)
           (transduce flatten (completing rf) result input)
           (rf result input))))))
Does the trick. But tbh, I think my original decision to just turn it into an rf and use reduce is probably more correct now I understand it. We are trying to recursively reduce. We just got lucky that the completion has done nothing in context up until now.