ClassCastException when presenting `tech.v3.dataset`
full repro at https://github.com/pieterbreed/clerk-dataset-repro by @pieterbreed
Also reported on Clojurians slack https://clojurians.slack.com/archives/C035GRLJEP8/p1724429010102259
This seems to be a smaller repro of what's going wrong:
(def ds
(let [ds (tech.v3.dataset/mapseq-parser)]
(doseq [x (for [i (range 100)]
{:x i
:y (java.time.Instant/now)})]
(ds x))
(ds)))
(into [] (take 3) (:x ds)) ;; works
(into [] (take 3) (:y ds)) ;; throws ClassCastException
I don't know enough about tech.v3.dataset to know why the type of column changes where I can do take and where I cannot.
Given the exception (during presentation)
Execution error (ClassCastException) at nextjournal.clerk.viewer/present+paginate-children (viewer.cljc:1627).
class clojure.lang.Reduced cannot be cast to class clojure.lang.ITransientCollection (clojure.lang.Reduced and clojure.lang.ITransientCollection are in unnamed module of loader 'app')
at nextjournal.clerk.viewer$present_PLUS_paginate_children.invokeStatic(viewer.cljc:1627)
at nextjournal.clerk.viewer$present_PLUS_paginate_children.invoke(viewer.cljc:1618)
at nextjournal.clerk.viewer$present_STAR_.invokeStatic(viewer.cljc:1691)
I guess it has to do with the into there. This change fixes showing the notebook, but maybe it's worth to dig a bit deeper.
diff --git a/src/nextjournal/clerk/viewer.cljc b/src/nextjournal/clerk/viewer.cljc
index bf431dcb..2a20c1e5 100644
--- a/src/nextjournal/clerk/viewer.cljc
+++ b/src/nextjournal/clerk/viewer.cljc
@@ -1623,11 +1623,11 @@
(update :n min @!budget))
children (if preserve-keys?
(into {} (map (fn [[k v]] [k (present* (inherit-opts wrapped-value v k))])) xs)
- (into []
- (comp (if paginate? (drop+take-xf fetch-opts') identity)
- (map-indexed (fn [i x] (present* (inherit-opts wrapped-value x (+ i (or offset 0))))))
- (remove nil?))
- (ensure-sorted xs)))
+ (vec
+ (sequence (comp (if paginate? (drop+take-xf fetch-opts') identity)
+ (map-indexed (fn [i x] (present* (inherit-opts wrapped-value x (+ i (or offset 0))))))
+ (remove nil?))
+ (ensure-sorted xs))))
and pagination also works here
Maybe the reduce from into receives conflicting calls to reduced.
Here is a more minimal repro, and a workaround:
user> (require '[tech.v3.dataset :as ds])
nil
user> (import 'java.time.Instant)
java.time.Instant
user> (def ds (ds/->dataset {:x (range 5) :y (repeatedly 5 #(java.time.Instant/now))}))
#'user/ds
user> ds
_unnamed [5 2]:
| :x | :y |
|---:|-----------------------------|
| 0 | 2024-08-29T15:54:57.987431Z |
| 1 | 2024-08-29T15:54:57.988432Z |
| 2 | 2024-08-29T15:54:57.988455Z |
| 3 | 2024-08-29T15:54:57.988458Z |
| 4 | 2024-08-29T15:54:57.988461Z |
user> (take 3 (:x ds))
(0 1 2)
user> (take 3 (:y ds))
(#object[java.time.Instant 0xf3f2e65 "2024-08-29T15:54:57.987431Z"]
#object[java.time.Instant 0x10dd3c7 "2024-08-29T15:54:57.988432Z"]
#object[java.time.Instant 0x1ff353ba "2024-08-29T15:54:57.988455Z"])
user> (into [] (take 3) (:x ds))
[0 1 2]
user> (into [] (take 3) (:y ds))
Execution error (ClassCastException) at user/eval59642 (form-init362550846257869959.clj:43).
class clojure.lang.Reduced cannot be cast to class clojure.lang.ITransientCollection (clojure.lang.Reduced and clojure.lang.ITransientCollection are in unnamed module of loader 'app')
user> (into [] (take 3) (vec (:y ds)))
[#object[java.time.Instant 0x6e058d1e "2024-08-29T15:54:57.987431Z"]
#object[java.time.Instant 0x6bb8eb57 "2024-08-29T15:54:57.988432Z"]
#object[java.time.Instant 0x59e0600a "2024-08-29T15:54:57.988455Z"]]
You can log this in https://github.com/techascent/tech.ml.dataset if you'd like.
Thanks @harold, I've opened the attached issue using your smaller repro.
:+1: - nice, thanks! Could be a good bug.