Box a primitive with metadata in Clojure.
Clojure Java
Switch branches/tags
Nothing to show
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
doc
src
test/metabox
.gitignore
LICENSE
README.md
project.clj

README.md

metabox

Clojure doesn't allow metadata on primitives. This tiny library provides a simple way to box a primitive and add metadata to it.

Summary

This library provides a simple* way to box a primitive that is also metadata friendly.

  • To box a value, use metabox/box.
  • To get the value, use deref or @.
  • To get the metadata, use meta.
  • To attach new metadata, use with-meta.

* This is a matter of perspective. If you find something simpler for you, let me know, or share on this Stack Overflow question: "Simplest possible Clojure object that can accept a primitive and metadata?".

Example

Now for an example that is universally applicable:

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

(Note that I'm using the var, critical-density, as a convenience. I want to attach the metadata to the value, not to the var.)

Implementation

Clojure metadata works with classes descending from clojure.lang.IObj. So, this code provides a MetaBox class that implements clojure.lang.IObj.

MetaBox also implements clojure.lang.IDeref because that provides deref, a convenient way to provide a way to get the value out of the box.

Alternatives

There are other ways to achieve boxing; for example, you might use a vector:

(def speed-of-light (with-meta [299792458] {:units "m/s"}))

There are at least two drawbacks with the vector approach:

  • Conceptual: You want boxing only, but a vector is not the right way to do it, conceptually, since it can hold more than one thing.
  • Implementation: A vector is not the simplest implementation that will work. If you look at how vectors are implemented in Java, you will see that PersistentVector implements clojure.lang.IObj but also adds other functionality not needed for boxing.

No matter how you do it, Howard, I hope that boxing has been good for you.

Motivation

I created this because I want to attach metadata to byte arrays. Clojure doesn't allow that because arrays are primitives.

Here is how this works with metabox:

(require '[metabox.core :refer (box)])
(def many-bytes (box (byte-array 100) {:charset "UTF-8"}))
(deref many-bytes) ; #<byte[] [B@4b4340e2>
(meta many-bytes) ; {:charset "UTF-8"}

I considered some alternatives, such as wrapping with Clojure maps and vectors, as mentiond above. I also looked at ArrayUtils/toObject from Apache Commons lang. But I don't want to copy the byte array; I simply want to box it. You could do something similarly unsatisying with Clojure's make-array function.

License

Copyright 2013 Bluemont Labs LLC

Distributed under the Eclipse Public License either version 1.0 or (at your option) any later version.