specter

2025-08-06T09:17:25.035969Z

Hi @nathanmarz I have a type that implements PersistentVector protocols (basically acts like a vector) and today we found a bug:

(sp/setval [sp/FIRST] {:x 3} (obj [{:x 2}])) => ({:x 3})
sp/FIRST returned a list and not a vector. we did a little digging and found that in UpdateExtremes:
(extend-protocol UpdateExtremes
  #?(:clj clojure.lang.IPersistentVector :cljs cljs.core/PersistentVector)
  (update-first [v afn]
    (update-first-vector v afn))
  (update-last [v afn]
    (update-last-vector v afn))

...

  #?(:clj Object :cljs default)
  (update-first [l val]
    (update-first-list l val))
  (update-last [l val]
    (update-last-list l val)))
it doesn't recognize my type as vector so it defaults to list impl. I extended the protocol to support my type:
(extend-protocol
 UpdateExtremes
  VectorObjWrapper
  (update-first [v afn] (update-first-vector v afn))
  (update-last [v afn] (update-last-vector v afn)))
but update-first-vector & update-last-vector are private so I ended up copying them:
(defn- update-first-vector [v afn]
  (let [val (nth v 0)
        newv (afn val)]
    (if (identical? sp/NONE newv)
      (subvec v 1)
      (assoc v 0 newv))))

(defn- update-last-vector [v afn]
  ;; type-hinting vec-count to ^int caused weird errors with case
  (let [c (int (vec-count v))]
    (case c
      1 (let [[e] v
              newe (afn e)]
          (if (identical? sp/NONE newe)
            []
            [newe]))
      2 (let [[e1 e2] v
              newe (afn e2)]
          (if (identical? sp/NONE newe)
            [e1]
            [e1 newe]))
      (let [i (dec c)
            newe (afn (nth v i))]
        (if (identical? sp/NONE newe)
          (pop v)
          (assoc v i newe))))))
Is this the best way to go about it? (I also have a Map type and I'm thinking about all the other protocols in specter that I should implement)

itaied 2025-09-27T10:16:32.405229Z

hey @nathanmarz we are facing this issue again with a different protocol (i guess) it's for a custom type of map that keypath doesn't add values for the path can you point me to which protocol should we extend?

(sp/setval [:x :y] 12 (obj {})) => nil

itaied 2025-09-27T11:00:36.764319Z

ok nevermind, i have managed to figure out how to apply it

2025-08-06T09:18:50.808899Z

@itai

nathanmarz 2025-08-06T16:07:33.096299Z

yea, you would need to extend the protocols the navigator uses to get the navigator working on different types

nathanmarz 2025-08-06T16:07:37.201429Z

or you could make a new navigator