juji-io/editscript

Repeated diffs applications on a vector crashes.

bowbahdoe opened this issue ยท 3 comments

This seems to be caused by https://clojure.atlassian.net/browse/CLJ-2065, but just documenting it here

(def current-state (atom [:div]))
=> #'ssr/current-state
(println (type @current-state))
clojure.lang.PersistentVector
=> nil
(defn render
  [new]
  (hiccup/html
   (swap! current-state
          (fn [html-1]
            (let [diff (editscript/diff html-1 new)]
              (editscript/patch html-1 diff))))))
=> #'ssr/render
(render [:div "b"])
=> "<div>b</div>"
(println (type @current-state))
clojure.lang.APersistentVector$SubVector
=> nil
(render [:div "c"])
Execution error (IllegalArgumentException) at editscript.diff.a-star/associative-children (a_star.cljc:86).
No implementation of method: :kv-reduce of protocol: #'clojure.core.protocols/IKVReduce found for class: clojure.lang.APersistentVector$SubVector

Adding the diff mentioned in the bug report will sidestep this, but idk if thats something a library would want to do.

(extend-type APersistentVector$SubVector
  IKVReduce
  (kv-reduce [subv f init]
   (let [cnt (.count subv)]
     (loop [k 0
            ret init]
       (if (< k cnt)
         (let [val (.nth subv k)
               ret (f ret k val)]
           (if (reduced? ret)
             @ret
             (recur (inc k) ret)))
         ret)))))
         ```

Clojure 1.11 should has a fix for this. Please confirm if 1.11 alpha does fix it.

Ran into this and Clojure 1.11.x indeed fixes this for me. ๐Ÿ‘๐Ÿป

(let [current-state (atom [:div])]                                                                                                                                                                                                                                                          
  (assert (= clojure.lang.PersistentVector(type @current-state)))                                                                                                                                                                                                                           
  (letfn [(render [new]                                                                                                                                                                                                                                                                     
            (hiccup.core/html                                                                                                                                                                                                                                                               
             (swap! current-state                                                                                                                                                                                                                                                           
                    (fn [html-1]                                                                                                                                                                                                                                                            
                      (let [diff (editscript.core/diff html-1 new)]                                                                                                                                                                                                                         
                        (editscript.core/patch html-1 diff))))))]                                                                                                                                                                                                                           
    (render [:div "b"])                                                                                                                                                                                                                                                                     
    (assert (= clojure.lang.APersistentVector$SubVector (type @current-state)))                                                                                                                                                                                                             
    (render [:div "c"]))) ;; => "<div>c</div>"     
(let [a [1 2 3]                                                                                                                                                                                                                                                                             
      b (subvec a 0 2)]                                                                                                                                                                                                                                                                     
  (editscript.core/diff a b)) ;; => [[[2] :-]]