eval in macro: No method for dispatch value

6 views
Skip to first unread message

Michiel de Mare

unread,
Dec 2, 2008, 8:22:47 PM12/2/08
to Clojure
After playing around with macros, I ran into this problem with Clojure
(the latest version from github). The following code throws an
IllegalArgumentException: "No method for dispatch value: class
java.lang.StringBuffer"

(defmacro wrap [h] (eval h))
(wrap (new StringBuffer))

But this works fine:

(eval (new StringBuffer))

Apparently this fails for StringBuffers, but not for Strings or
ArrayLists. I'm not sure if this is a bug, or what the underlying
rules here are. At the very least the error message is ... unhelpful.
I hope that somebody can shine a light on this.

Cheers,

Michiel

Stuart Halloway

unread,
Dec 2, 2008, 9:22:24 PM12/2/08
to clo...@googlegroups.com
Hi Michiel,

eval is for form data, so I am more surprised that it works for
ArrayList than that it fails for StringBuffer.

Stu

Michiel de Mare

unread,
Dec 3, 2008, 6:04:36 AM12/3/08
to Clojure
This has nothing to do with eval. The following fails too:

(defmacro foo [] (new StringBuffer))
(foo)

Meikel Brandmeyer

unread,
Dec 3, 2008, 6:23:45 AM12/3/08
to Clojure
Hi,

On 3 Dez., 12:04, Michiel de Mare <merl...@gmail.com> wrote:
> This has nothing to do with eval. The following fails too:
>
> (defmacro foo [] (new StringBuffer))
> (foo)

The results at the Repl are printed via pr-on and
obviously print-dup doesn't know how to print a
StringBuffer.

Sincerely
Meikel

Michiel de Mare

unread,
Dec 3, 2008, 6:58:52 AM12/3/08
to Clojure
Indeed, (print-dup (new StringBuffer) (new java.io.StringWriter))
throws the same exception.
But (new StringBuffer) in the REPL prints the stringbuffer just fine.

Why does the exception only get triggered in macros?

And doesn't print-dup need a default for unknown classes?

Rich Hickey

unread,
Dec 3, 2008, 8:47:24 AM12/3/08
to Clojure
The purpose of a macro is to transform a form into another form on
behalf of the compiler.

If you want a call to (foo), to become a call to create a new
StringBuilder, you need to return a piece of data that is a call to
new, i.e. a list:

i.e. you want to write this form:

(foo)

you want it to expand into this form, i.e. the list of the symbols
'new and 'StringBuilder:

(new StringBuilder)

And the way to do that is this:

(defmacro foo []
`(new StringBuilder))

(macroexpand '(foo))
-> (new java.lang.StringBuilder)

But that's not what this does:

(defmacro foo []
(new StringBuilder))

That turns the list (foo) into a StringBuilder object, and asks the
compiler to compile that StringBuilder object:

(macroexpand '(foo))
-> #<StringBuilder >

As it says at:

http://clojure.org/evaluation

"Any object other than those discussed above will evaluate to itself."

So what you are asking the compiler to do when asking it to compile a
literal object like that, is to put that object into the compiled
bytecode. It ends up that very few objects can be represented in
bytecode - basically the primitives and Strings. So a language that
allows for literal objects other than those (as does Clojure) needs to
find a way to put them into the bytecode and restore them later.
Clojure does this by representing them as Strings, specifically by
print-duping them and reading them later. Therefor the only objects
that can be literals in code are those that support print-dup.

This has nothing to do with printing objects at the repl - that does
not use print-dup, just print-method.

Unfortunately there is no universally valid default definition for
print-dup. (a default I tried, printing a call to the ctor with the
toString value, works often, but not always). And were the default to
print something that failed at read time, those failures occur during
class static initialization time, where you really want to avoid
errors.

print-dup supports the vast majority of things one would like to treat
as constants in code, with some notable pseudo-value classes like Date
still to come, but embedding a literal StringBuffer in code is most
certainly a mistake.

Rich


Michiel de Mare

unread,
Dec 3, 2008, 10:39:36 AM12/3/08
to Clojure
Thank you very much for this detailed explanation!

Michiel de Mare
Reply all
Reply to author
Forward
0 new messages