Metadata

14 views
Skip to first unread message

a r

unread,
Apr 17, 2008, 6:37:43 PM4/17/08
to clo...@googlegroups.com
Hi,

It looks like metadata cannot be attached to primitive values:
(with-meta "abc" {:test :test})
java.lang.ClassCastException: java.lang.String cannot be cast to
clojure.lang.IObj

Is there any reason for primitive values to be excluded (except for
implementation difficulties)?

Also, what are the rules for "propagating" or discarding metadata?
I have noticed that "cons" propagates metadata attached to its second
argument (only) to its value while "concat" does not (although they
still may be present somewhere in the result):

user=> (def a (with-meta '(1) {:test :test}))
#<Var: user/a>
user=> (def b (with-meta '(2 3) {:test2 :test2}))
#<Var: user/b>
user=> (def c (concat a b))
#<Var: user/c>
user=> ^c
nil
user=> ^(first c)
nil
user=> ^(rest c)
{:test2 :test2}

Cheers,
-r.

Rich Hickey

unread,
Apr 17, 2008, 8:21:49 PM4/17/08
to Clojure


On Apr 17, 6:37 pm, "a r" <nbs.pub...@gmail.com> wrote:
> Hi,
>
> It looks like metadata cannot be attached to primitive values:
> (with-meta "abc" {:test :test})
> java.lang.ClassCastException: java.lang.String cannot be cast to
> clojure.lang.IObj
>
> Is there any reason for primitive values to be excluded (except for
> implementation difficulties)?
>

Right. Metadata is supported only by collections and symbols. Some of
the Clojure types are reference types, and metadata is a value thing,
and Strings etc are Java classes I cannot modify.

> Also, what are the rules for "propagating" or discarding metadata?

Metadata should be preserved across all collection 'modifications'.

Note that collection metadata does not transfer to its seq, because
the seq is (usually) not the collection.

> I have noticed that "cons" propagates metadata attached to its second
> argument (only) to its value while "concat" does not (although they
> still may be present somewhere in the result):
>

cons/conj is a modification of (addition to) its argument.

concat is a function of n sequences and adopts none of their metadata.

Metadata is never automatically merged.

Rich

a r

unread,
May 4, 2008, 5:50:54 PM5/4/08
to clo...@googlegroups.com
On Fri, Apr 18, 2008 at 1:21 AM, Rich Hickey <richh...@gmail.com> wrote:
> >
> > It looks like metadata cannot be attached to primitive values:
> > (with-meta "abc" {:test :test})
> > java.lang.ClassCastException: java.lang.String cannot be cast to
> > clojure.lang.IObj
> >
> > Is there any reason for primitive values to be excluded (except for
> > implementation difficulties)?
> >
>
> Right. Metadata is supported only by collections and symbols. Some of
> the Clojure types are reference types, and metadata is a value thing,
> and Strings etc are Java classes I cannot modify.

I'm sorry for the delay. I was playing with clojure a bit, actually
trying to write some non-trivial (for me) code.

While I understand that adding metadata to primitive values is
difficult, I found lack of this possibility a bit disturbing. Please
consider following scenario:

I would like to process generic data that are either primitive
scalars, vectors or maps with quite a lot of less frequently used data
attached (i.e. metadata). However, because I can't add metadata to
primitive values I have to revert to wrapping these primitive values
with maps. Once I have maps there - there is no appealing reason for
me to add metadata to them - all necessary information can be added
directly to map. Then, to make the code more generic I wrap lists and
vectors with maps again. This way I get a working, although ugly,
solution which doesn't use metadata at all and where all values have a
following form:

{:val <primitive value, vector, list or map>,
:meta1 <...>, :meta2 <...>, ...}

This allows me to access both value and "metadata" in a uniform way
regardless of the value type. Unfortunately, retrieving the value
alone is much more elaborate than it would be if I could simply use
metadata construct in the first place. This additional complexity is
especially painful when these data are part of a public interface in
my code.

Do you think this use case is legitimate enough to try to change the
current metadata mechanism? If not, how would you design a cleaner
data structure that can either be a primitive scalar value or a vector
and has metadata attached?

Regards,
-r.

Rich Hickey

unread,
May 4, 2008, 7:38:07 PM5/4/08
to Clojure


On May 4, 5:50 pm, "a r" <nbs.pub...@gmail.com> wrote:
No. Clojure generally defers to the pragmatic - wrapping all
primitives with metadata-capable wrappers is a lot of overhead, and
there are big interoperability costs as well, so don't expect that to
change.

> If not, how would you design a cleaner
> data structure that can either be a primitive scalar value or a vector
> and has metadata attached?
>

Clojure vectors support metadata. Are you really going to attach
metadata to ints and strings? For what kind of information? It seems
to me there is likely a semantic path to such primitives, e.g. a
number is an age or a string a name. If you are using maps to hold
these primitives, you might want to consider using symbols (not
keywords) as the keys, e.g. {'name "fred" 'age 42}. If you do, you can
then attach metadata to the labels rather than the values, since
symbols support metadata.

Rich

a r

unread,
May 4, 2008, 8:17:46 PM5/4/08
to clo...@googlegroups.com
On Mon, May 5, 2008 at 12:38 AM, Rich Hickey <richh...@gmail.com> wrote:
>
> > Do you think this use case is legitimate enough to try to change the
> > current metadata mechanism?
>
> No. Clojure generally defers to the pragmatic - wrapping all
> primitives with metadata-capable wrappers is a lot of overhead, and
> there are big interoperability costs as well, so don't expect that to
> change.

OK. Sorry for asking. I thought that since Clojure is using boxed data
types there might have been some flexibility in this area.

> > If not, how would you design a cleaner
> > data structure that can either be a primitive scalar value or a vector
> > and has metadata attached?
> >
>
> Clojure vectors support metadata. Are you really going to attach
> metadata to ints and strings? For what kind of information?

I wrote a scheme reader which parses s-expressions and returns a tree
of tokens with embedded additional information (file names,
line/column numbers, original (textual) representation etc.). The
problem is that these tokens include strings and numerals that do not
support attaching metadata. Currently I am wrapping each symbol with a
map, where I can embed additional information even without clojure's
metadata. That works reasonably well (i.e. I can live without
primitive values with metadata) but makes interface between reader and
whatever procedure consuming its output more complicated (I need to
specify the format or each token or build an abstraction layer).
Ideally, at the reader's output I'd like to have a simple tree of
symbols/numbers/strings with all other information available through
metadata.

Thanks,
-r.

Reply all
Reply to author
Forward
0 new messages