jdevuyst/ruminant

No concat?

johanatan opened this issue · 2 comments

Suppose I wanted to remove an item in the middle of a vector. The canonical way to do so in Clojure would be:

(defn vec-remove
  "remove elem in coll"
  [coll pos]
  (vec (concat (subvec coll 0 pos) (subvec coll (inc pos)))))

yet there is no concat operator for Subvec in ruminant. Is there a better way to achieve this?

In Clojure, if you call concat on two vectors then you end up with a different data structure:

user=> (type (concat [1 2] [3]))
clojure.lang.LazySeq

Ruminant does not include lazy sequences, however, and I suppose that is why there's no concat.

That being said, as you may know, when you call vec, the lazy list is transformed into a vector. This isn't particularly fast though. It's done through one of the create methods of the PersistentVector class (via vec and LazilyPersistentVector).

It's quite similar to calling into:

(defn vec-remove
   [coll pos]
   (into (subvec coll 0 pos) (subvec coll (inc pos))))

So maybe we should add into. That could be done as follows:

  • If the first vector can be made transient (it is a proper PersistentVector), make it transient and conj all elements from the second vector onto it (via makeIterator()), and then make the result persistent
  • If the first vector cannot be made transient (it's a subvec), conj all elements from the second vector onto it.

This is what Clojure's into does.

An alternative approach would be to implement clojure.core.reducers.cat. The Clojure implementation only implements a few methods but it should be possible to implement all of PersistentVectorType.

I think that both approaches might be fun exercises that shouldn't be too hard to do, if you feel like it. :)

To summarize. There are two approaches:

  • We could add a method into to the PersistentVectorType protocol. This would have a default implementation that conj'd elements of the second vector onto the first one. A specialized version for PersistentVector would make use of transient vectors.
  • We could add a new struct Cat that would have two members of type PersistentVectorType