Wrapping a proxy expression in macrology

27 views
Skip to first unread message

Joubert Nel

unread,
May 1, 2008, 12:01:56 AM5/1/08
to Clojure
Hi,

I'm trying to compose a macro to deal with a proxy definition. The
reason I am doing this is that the body of the proxy definition
includes expressions passed as macro arguments, the evaluation of
which I need to control.

Anyway, with a macro, the resulting proxy's members do not seem to be
accessible. I suspect I am doing something silly.

Here is an example which illustrates the problem.
Assume I want to generate the following proxy expression:

(proxy [java.util.zip.Checksum] []
(getValue [] 1)
(reset [] nil)
(update
([b off len] nil)
([b] nil))))

If I (def) a symbol to the above, e.g. (def check1 (proxy....)), I am
then able to successfully express:
(. check1 (getValue))


Now, let's say I want to wrap this in macrology like so:

(defmacro describe-checksum []
`(proxy [java.util.zip.Checksum] []
(getValue [] 1)
(reset [] nil)
(update
([b off len] nil)
([b] nil))))

If I (def check2 describe-checksum) and then express (. check2
(getValue)), I get this backtrace:

java.lang.IllegalArgumentException: No matching method found: getValue
at clojure.lang.Reflector.invokeMatchingMethod(Reflector.java:35)
at clojure.lang.Reflector.invokeInstanceMethod(Reflector.java:26)
at clojure.lang.Compiler$InstanceMethodExpr.eval(Compiler.java:992)
at clojure.lang.Compiler.eval(Compiler.java:3385)
at clojure.fns.clojure.eval__375.invoke(boot.clj:1181)
at clojure.fns.swank.slime_eval__1156.invoke(swank-clojure.clj:365)
at clojure.fns.swank.fn__1158$listener_eval__1160.doInvoke(swank-
clojure.clj:374)
at clojure.lang.RestFn.invoke(RestFn.java:415)
at clojure.lang.AFn.applyToHelper(AFn.java:183)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at clojure.fns.clojure.apply__97.doInvoke(boot.clj:364)
at clojure.lang.RestFn.invoke(RestFn.java:428)
at clojure.fns.swank.emacs_rex__1136.doInvoke(swank-clojure.clj:301)
at clojure.lang.RestFn.invoke(RestFn.java:466)
at clojure.lang.AFn.applyToHelper(AFn.java:194)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at clojure.fns.clojure.apply__97.doInvoke(boot.clj:364)
at clojure.lang.RestFn.invoke(RestFn.java:428)
at clojure.fns.swank.dispatch__1138.invoke(swank-clojure.clj:318)
at clojure.fns.swank.swank_handler__1142.invoke(swank-clojure.clj:
330)
at clojure.fns.swank.accept_connections__1093.invoke(swank-
clojure.clj:166)
at clojure.fns.swank.start_swank__1147.invoke(swank-clojure.clj:339)
at clojure.fns.user.fn__1283.invoke(Unknown Source)
at clojure.lang.AFn.run(AFn.java:37)
at java.lang.Thread.run(Thread.java:613)

My first thought was that I had incorrectly formulated the macro, but
it appears OK according to the following macroexpansion:

dbtest> (macroexpand-1 '(describe-checksum))
(clojure/proxy [java.util.zip.Checksum] [] (clojure/getValue [] 1)
(clojure/reset [] nil) (clojure/update ([clojure/b clojure/off clojure/
len] nil) ([clojure/b] nil)))
dbtest>

=======

Any ideas where I am going wrong?

Joubert

PS: if Google Groups supported rich formatting I could make this post
far more readable.

Christophe Grand

unread,
May 1, 2008, 3:27:06 AM5/1/08
to clo...@googlegroups.com
Joubert Nel a écrit :
> Hi,

>
> Now, let's say I want to wrap this in macrology like so:
>
> (defmacro describe-checksum []
> `(proxy [java.util.zip.Checksum] []
> (getValue [] 1)
> (reset [] nil)
> (update
> ([b off len] nil)
> ([b] nil))))
>
> If I (def check2 describe-checksum) and then express (. check2
> (getValue)), I get this backtrace:
>
> java.lang.IllegalArgumentException: No matching method found: getValue
>
(def check2 describe-checksum) does define a var whose value is the
macro function itself not the result of evaluating its expansion.
(def check2 (describe-checksum)) is what you want but then you get
clojure.lang.Compiler$CompilerException: NO_SOURCE_FILE:242: Can't use
qualified name as parameter.
The cause of this error can be seen with macroexpand-1:

> dbtest> (macroexpand-1 '(describe-checksum))
> (clojure/proxy [java.util.zip.Checksum] [] (clojure/getValue [] 1)
> (clojure/reset [] nil) (clojure/update ([clojure/b clojure/off clojure/
> len] nil) ([clojure/b] nil)))
>
where you should have got:

dbtest> (macroexpand-1 '(describe-checksum))
(clojure/proxy [java.util.zip.Checksum] [] (getValue [] 1)


(reset [] nil) (update ([b off len] nil) ([b] nil)))


only the symbol 'proxy has to be resolved but syntax-quote resolve them all.
You can prevent resolution of a symbol inside a syntax quote by
prefixing it with ~' but, if the real name of your symbol as no
importance, better use autogensyms.

(defmacro describe-checksum []
`(proxy [java.util.zip.Checksum] []
(~'getValue [] 1)
(~'reset [] nil)
(~'update
([b# off# len#] nil)
([b#] nil))))

user=> (macroexpand-1 '(describe-checksum))
(clojure/proxy [java.util.zip.Checksum] []
(getValue [] 1)
(reset [] nil)
(update ([b__1621 off__1622 len__1623] nil) ([b__1621] nil)))
user=> (def check2 (describe-checksum))
#'user/check2
user=> (. check2 (getValue))
1

Christophe

Joubert Nel

unread,
May 1, 2008, 9:30:34 AM5/1/08
to Clojure
HI Christophe,

On May 1, 3:27 am, Christophe Grand <christo...@cgrand.net> wrote:

> (def check2 describe-checksum) does define a var whose value is the
> macro function itself not the result of evaluating its expansion.
> (def check2 (describe-checksum)) is what you want but then you get

Yes, sorry about the typo.

> clojure.lang.Compiler$CompilerException: NO_SOURCE_FILE:242: Can't use
> qualified name as parameter.
> The cause of this error can be seen with macroexpand-1:> dbtest> (macroexpand-1 '(describe-checksum))
> > (clojure/proxy [java.util.zip.Checksum] [] (clojure/getValue [] 1)
> > (clojure/reset [] nil) (clojure/update ([clojure/b clojure/off clojure/
> > len] nil) ([clojure/b] nil)))

I did notice the clojure/ prefixed to the proxy "method" names, and
was suspicious, but wasn't sure about it.

>
> where you should have got:
>
> dbtest> (macroexpand-1 '(describe-checksum))
> (clojure/proxy [java.util.zip.Checksum] [] (getValue [] 1)
> (reset [] nil) (update ([b off len] nil) ([b] nil)))
>
> only the symbol 'proxy has to be resolved but syntax-quote resolve them all.

Why does this happen? If I have some other constructs in backquote
syntax, e.g. a (let), or a (try), etc. then there's no need to unquote
these symbols, like seen in the snippet below. This feels different to
me than in CL, which isn't necessarily an issue in itself.

user> (defmacro describe-checksum []
`(proxy [java.util.zip.Checksum] []
(~'getValue []
(let [x# 1]
x#))
(reset [] nil)
(update
([b# off# len#] nil)
([b#] nil))))

> You can prevent resolution of a symbol inside a syntax quote by
> prefixing it with ~' but, if the real name of your symbol as no
> importance, better use autogensyms.
>
> (defmacro describe-checksum []
>           `(proxy [java.util.zip.Checksum] []
>                   (~'getValue [] 1)
>                   (~'reset [] nil)
>                   (~'update

Yeah, this is where I got caught. In CL, backquote syntax behaves very
nicely in a template-like manner. I do not understand why I need to
unquote the getValue, reset, and update symbols.

Joubert

Rich Hickey

unread,
May 1, 2008, 9:43:01 AM5/1/08
to Clojure
CL is a Lisp-2. Function names are in a separate namespace, so a local
var named 'list wouldn't conflict with a call to the function named
'list emitted by a macro. Clojure is a Lisp-1, where it would, if the
macro-emitted 'list wasn't namespace-qualified. CL-style procedural
macros are difficult to make hygienic in a Lisp-1, and Clojure's
syntax-quote is an effort to remedy that, and it works well. It does
mean that when you want names to escape unresolved you have to do
extra work - that's the tradeoff.

Clojure also differs from CL in that symbols are not vars, and the
reader does not have interning side-effects.

Rich
Reply all
Reply to author
Forward
0 new messages