# Tagged literal for \`clojure.core.Vec\` -not possible for Clojure?

**URL:** https://clojureverse.org/t/tagged-literal-for-clojure-core-vec-not-possible-for-clojure/6452
**Category:** How to?
**Tags:** hexstring, tagged-literal
**Created:** [August 28, 2020, 2:14am UTC](https://clojureverse.org/t/tagged-literal-for-clojure-core-vec-not-possible-for-clojure/6452 "2020-08-28T02:14:46Z")
**Posts on this page:** 11
**Page:** 1

<div class="post-metadata">

### Author: ![Chris\_Hapgood](https://clojureverse.org/user_avatar/clojureverse.org/chris_hapgood/32/1284_2.png) [@Chris\_Hapgood](https://clojureverse.org/u/Chris_Hapgood)
#### Post date: [August 28, 2020, 2:14am UTC](https://clojureverse.org/t/tagged-literal-for-clojure-core-vec-not-possible-for-clojure/6452/1 "2020-08-28T02:14:46Z")

</div>

I’m struggling with building a reader function for a tagged literal that will yield a `clojure.core.Vec` -which, when initialized to hold `:byte`, is a pretty close stand-in for a Java native byte array but with the nice property of being immutable and following Clojure equality semantics.

Regardless of my reader function, the resulting value is always a `clojure.lang.PersistentVector` -which is not at all the same thing.

Here’s a quick reader function you can try that looks like it _should_ work:

```auto
(defn read-bytes [bs] (apply vector-of :byte (map byte bs)))

```

Then add an appropriate `data_readers.clj` entry:

```auto
{cch/bytes cch.bytes/read-bytes}

```

Then, from the REPL, type in

```auto
(class #cch/bytes [-1 0 1])

```

I am surprised by the result, especially since this yields the expected answer:

```auto
(read-string "#cch/bytes [1 0 -1]")

```

It’s as though the Clojure reader cycles through the print/read process a second time after the reader function has done its job. But even defining `print-dup` for `clojure.core.Vec` doesn’t seem to resolve the problem. Here’s my attempt:

```auto
(defmethod print-dup clojure.core.Vec
  [^clojure.core.Vec v ^java.io.Writer w]
  (.write w "#cch/bytes ")
  (.write w (str v)))

```

In fact, it doesn’t seem to matter what I use for the print-dup implementation -Clojure always prints a `clojure.core.Vec` as a `clojure.lang.PersistentVector`.

So I guess there are really two questions: 1. Why can’t I supply a `print-dup` for `clojure.core.Vec`? and 2. Why is my tagged literal reader function not sufficient to yield a `clojure.core.Vec`?

---

<div class="post-metadata">

### Author: ![joinr](https://clojureverse.org/user_avatar/clojureverse.org/joinr/32/1861_2.png) [@joinr](https://clojureverse.org/u/joinr)
#### Post date: [August 28, 2020, 4:13am UTC](https://clojureverse.org/t/tagged-literal-for-clojure-core-vec-not-possible-for-clojure/6452/2 "2020-08-28T04:13:11Z")

</div>

I had a similar double-printing phenomenon where the read-bytes function was invoked 2x. I’m guessing that there’s some special processing happening in the reader logic that’s maybe getting confused by using Vecs which derive from vectors…

Out of curiosity (just avoiding any problems that might happen if you are working on something that inherits from vector), if you define your own typed container that wraps a byte array:

```
(deftype bytevector [^bytes bs]
  clojure.lang.Counted
  (count [this] (alength bs))
  clojure.lang.Indexed
  (nth [this idx] (aget bs idx))
  (nth [this idx not-found]
    (if (and (pos? idx)
             (< idx (alength bs)))
      (aget bs idx)
      not-found))
  clojure.lang.Seqable
  (seq [this] (seq bs)))

(defn read-bytes [xs]
  (bytevector. (byte-array xs)))

(defmethod print-method bytevector [v ^java.io.Writer w]
  (.write w (str "#user/bytevector"(vec (seq v)))))

(defmethod print-dup bytevector [o w]
  (print-ctor o (fn [o w] (.write w "(byte-array ")
                  (print-dup (vec (seq o)) w)
                  (.write w ")")) w))

(defn tst []
  (let [res (binding [*data-readers* {'user/bytevector user/read-bytes}]
              (read-string "#user/bytevector[0 0 1]"))]
    (println [(type res) res])
    res))

```

It seems to work okay:

```
;; user=> (tst)
;; [user.bytevector #user/bytevector[0 0 1]]
;; #user/bytevector[0 0 1]

```

Keep in mind that if you are using something like Cider, it will override the default print method and use `pprint` when it prints to your REPL in emacs; it may be necessary to implement a pprint method as well (this caused some headaches with defining custom printers for record types that seemed to never work, when in fact you have to define a print-method and a pprint method for cider to pick it up lol).

edit:

Funny enough, this seems to work fine!

```
(defn other-tst []
  (let [res (binding [*data-readers* {'user/bytevector (fn [xs] (into (vector-of :byte) (map byte xs)))}]
              (read-string "#user/bytevector[0 0 1]"))]
    (println [(type res) res])
    res))

;; user=> (def res (other-tst))
;; [clojure.core.Vec [0 0 1]]
;; #'user/res

```

Wondering if maybe your call to `apply` isn’t doing something unexpected somehow.

---

<div class="post-metadata">

### Author: ![Chris\_Hapgood](https://clojureverse.org/user_avatar/clojureverse.org/chris_hapgood/32/1284_2.png) [@Chris\_Hapgood](https://clojureverse.org/u/Chris_Hapgood)
#### Post date: [August 29, 2020, 1:43am UTC](https://clojureverse.org/t/tagged-literal-for-clojure-core-vec-not-possible-for-clojure/6452/3 "2020-08-29T01:43:33Z")

</div>

My experience is that `read-string` works where direct input into the REPL fails. I do not know why.

After setting my `data_readers.clj` to reference a var with the implementation @joinr supplied (including the `apply`) the result of any REPL evaluation is a `clojure.lang.PersistentVector`.

---

<div class="post-metadata">

### Author: ![joinr](https://clojureverse.org/user_avatar/clojureverse.org/joinr/32/1861_2.png) [@joinr](https://clojureverse.org/u/joinr)
#### Post date: [August 29, 2020, 4:38am UTC](https://clojureverse.org/t/tagged-literal-for-clojure-core-vec-not-possible-for-clojure/6452/4 "2020-08-29T04:38:55Z")

</div>

Funny enough, if you map `type` over the resulting persistent vector, they are all Byte types.

---

<div class="post-metadata">

### Author: ![Chris\_Hapgood](https://clojureverse.org/user_avatar/clojureverse.org/chris_hapgood/32/1284_2.png) [@Chris\_Hapgood](https://clojureverse.org/u/Chris_Hapgood)
#### Post date: [August 29, 2020, 7:17pm UTC](https://clojureverse.org/t/tagged-literal-for-clojure-core-vec-not-possible-for-clojure/6452/5 "2020-08-29T19:17:42Z")

</div>

That is really weird. It’s probably an artifact resulting from the mechanism that transmutes the vector as a whole into a `clojure.lang.PersistentVector`.

---

<div class="post-metadata">

### Author: ![joinr](https://clojureverse.org/user_avatar/clojureverse.org/joinr/32/1861_2.png) [@joinr](https://clojureverse.org/u/joinr)
#### Post date: [August 29, 2020, 11:02pm UTC](https://clojureverse.org/t/tagged-literal-for-clojure-core-vec-not-possible-for-clojure/6452/6 "2020-08-29T23:02:44Z")

</div>

I think I figured it out (partly). I remember from implementing clcojure that vectors have different evaluation semantics (as do other persistent structures). So when you “read” a vector, you may be computing a byte-vector (hence the byte coercions here) at read time, but that Vec result is then [“eval’d”](https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Compiler.java#L3220) from the REPL because (I think) it looks like a form that [implements IPersistentVector](https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Compiler.java#L3254) which is caught during [analyze during parsing](https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Compiler.java#L6791) yielding a clojure.lang.Compiler$VectorExpr, and when `eval` is invoked on the VectorExpr, it implicitly returns a persistent vector with boxed types. So that’s the problem, and it’s why `read-string` works (the resulting Vec is not eval’d). So

```
user=> (def the-vec (apply vector-of :byte [0 1 0]))
#'user/the-vec
user=> the-vec
[0 1 0]
user=> (type the-vec)
clojure.core.Vec
user=> (type (eval the-vec))
clojure.lang.PersistentVector
user=> (type the-vec)
clojure.core.Vec

user=> (defn read-bytes [bs] `(apply vector-of :byte (map byte ~bs)))
#'user/read-bytes
user=> #user/bytevector[0 1 0]
[0 1 0]
user=> (def res #user/bytevector[0 1 0])
#'user/res
user=> (type res)
clojure.core.Vec

```

Ugh, getting `print-dup` working with a deftype that wraps a either a primitive vector or a byte array is a pain. That seems to be the only way to work around the built-in parsing of IPersistentVectors at the moment…

---

<div class="post-metadata">

### Author: ![Chris\_Hapgood](https://clojureverse.org/user_avatar/clojureverse.org/chris_hapgood/32/1284_2.png) [@Chris\_Hapgood](https://clojureverse.org/u/Chris_Hapgood)
#### Post date: [August 30, 2020, 12:42am UTC](https://clojureverse.org/t/tagged-literal-for-clojure-core-vec-not-possible-for-clojure/6452/7 "2020-08-30T00:42:27Z")

</div>

That’s impressive analysis. I wonder why `eval` is called on the `Compiler$VectorExpr` and whether the same collapsing of types happens for associative types as well. Either way, it seems like an intractable problem that will limit my ability to use `clojure.core.Vec` as a more Clojure-idiomatic byte array.

---

<div class="post-metadata">

### Author: ![joinr](https://clojureverse.org/user_avatar/clojureverse.org/joinr/32/1861_2.png) [@joinr](https://clojureverse.org/u/joinr)
#### Post date: [August 30, 2020, 1:13am UTC](https://clojureverse.org/t/tagged-literal-for-clojure-core-vec-not-possible-for-clojure/6452/8 "2020-08-30T01:13:18Z")

</div>

It does happen with the other interfaces for persistent structures, since they are a sort of pseudo form. I would not say it’s intractable to use Vec, just not directly since it will get caught by the analyzer in this way. The approach I am looking at, which almost works, is to just wrap the Vec in a type the implements the interfaces of a vector, but not IPersistentVector. This allows the wrapped Vec to participate in common functions, while being left unevaluated after read (like a constant). If I can figure out the gripes from print-dup, it seems viable.

---

<div class="post-metadata">

### Author: ![Chris\_Hapgood](https://clojureverse.org/user_avatar/clojureverse.org/chris_hapgood/32/1284_2.png) [@Chris\_Hapgood](https://clojureverse.org/u/Chris_Hapgood)
#### Post date: [August 31, 2020, 7:39pm UTC](https://clojureverse.org/t/tagged-literal-for-clojure-core-vec-not-possible-for-clojure/6452/9 "2020-08-31T19:39:05Z")

</div>

> [@joinr](#):
>
> Ugh, getting `print-dup` working with a deftype that wraps a either a primitive vector or a byte array is a pain. That seems to be the only way to work around the built-in parsing of IPersistentVectors at the moment…

Did you find a way to get `print-dup` working with a deftype that implements `clojure.lang.ISeq` or similar? I cannot…

---

<div class="post-metadata">

### Author: ![andy.fingerhut](https://clojureverse.org/user_avatar/clojureverse.org/andy.fingerhut/32/1244_2.png) [@andy.fingerhut](https://clojureverse.org/u/andy.fingerhut)
#### Post date: [September 14, 2020, 3:44pm UTC](https://clojureverse.org/t/tagged-literal-for-clojure-core-vec-not-possible-for-clojure/6452/10 "2020-09-14T15:44:36Z")

</div>

I’ve added some details here: [https://ask.clojure.org/index.php/9567/possible-create-tagged-literal-reader-for-clojure-core-vec](https://ask.clojure.org/index.php/9567/possible-create-tagged-literal-reader-for-clojure-core-vec) and here: [https://github.com/jafingerhut/vec-data-reader](https://github.com/jafingerhut/vec-data-reader)

It seems to do what you are hoping for requires either a separate implementation of primitive vectors in Clojure that is designed with this use case in mind, or changes to the Java code that implements Clojure.

---

<div class="post-metadata">

### Author: ![system](https://clojureverse.org/uploads/default/original/2X/5/51079bf9e4b7d9466242c06cf1e43b9f8bd6da14.png) [@system](https://clojureverse.org/u/system)
#### Post date: [March 16, 2021, 3:44am UTC](https://clojureverse.org/t/tagged-literal-for-clojure-core-vec-not-possible-for-clojure/6452/11 "2021-03-16T03:44:39Z")

</div>

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.
