Simplest possible Clojure object that can accept a

2019-04-30 16:36发布

问题:

I want to add metadata to a byte array in Clojure. Since this is not allowed, one option I want to try is the simplest object wrapper that could work.

Here is the source code for with-meta.

That made me start looking at Clojure.lang.IObj. I haven't found what I want yet.

回答1:

Here's how you can create a deftype that supports metadata.

(import '(java.io Writer))

(deftype Box [value _meta]
  clojure.lang.IObj
  (meta [_] _meta)
  (withMeta [_ m] (Box. value m))
  clojure.lang.IDeref
  (deref [_] value)
  Object
  (toString [this]
    (str (.getName (class this))
         ": "
         (pr-str value))))

(defmethod print-method Box [o, ^Writer w]
  (.write w "#<")
  (.write w (.getName (class o)))
  (.write w ": ")
  (.write w (-> o deref pr-str))
  (.write w ">"))

(defn box
  ([value] (box value nil))
  ([value meta] (Box. value meta)))

And here's some example usage:

user> (def boxed (box (->> (range 5)
                        (map byte)
                        (byte-array))
                      {:stuff :foo}))
#<Var@1acd39b: #<Box@c50aa1: #>>
user> @boxed
[0, 1, 2, 3, 4]
user> (meta boxed)
{:stuff :foo}
user> (meta (with-meta boxed {:stuff :bar}))
{:stuff :bar}

This is the simplest way I can think of to put metadata on a byte array (reify doesn't work with clojure.lang.IObj and records include more unrelated functionality).

Another option (and perhaps simpler depending on the context) would be to store the byte array in a map, with the metadata either right alongside it or as actual metadata.



回答2:

After discussing with some people on #clojure IRC, I wrote a simple Java class, MetaBox, that implements clojure.lang.IObj. You can use this easily in Clojure with metabox/box and metabox/val as well as the usual metadata functions such as meta and with-meta.

; [metabox "0.1.0"]
(require '[metabox.core :refer (box val)])
(def critical-density (box 0.692 {:uncertainty 0.01}))
(val critical-density) ; 0.692
(meta critical-density) ; {:uncertainty 0.01}

You can find the source code and README over at clj-metabox.

UPDATE: Thanks to a few discussions and suggestion (see below), as of version 0.2.0, the API uses deref instead of val:

; [metabox "0.2.0"]
(require '[metabox.core :refer (box)])
(def critical-density (box 0.692 {:uncertainty 0.01}))
@critical-density ; 0.692
(meta critical-density) ; {:uncertainty 0.01}