7
votes

I have a problem constructing a DSL in Clojure. This is the concrete problem I have isolated from everything else.

Let's say we hava a simple macro:

user> (defmacro m1 [x] `'~x)
#'user/m1 

it just returns the literal supplied user> (m1 toUpperCase) toUpperCase

if we call java method for object everything works as expected

user> (. "a" toUpperCase)
"A"

but if we substitute method name for macro call that returns the methodname

user> (. "a" (m1 toUpperCase))

; Evaluation aborted.
Unable to resolve symbol: toUpperCase in this context

I want to use some java library that has fluent interface like a().b().c(). This maps to Clojure as:

(.. obj method1 method2 method3....etc)

I want to create macros that substitute some parts of this chain so my code should be like:

(.. obj method1 macro1)

and that should expand to

(.. obj method1 method2 method3)

definline also doesn't help. I tried that also

1

1 Answers

6
votes

The reason you're running into this problem is that the . special form does not evaluate its second argument (the symbol specifying the method or field) in the way you expect: it sees it as a call of the METHOD m1, with the ARGUMENT toUppercase. Because of that, you cannot generate the symbol for the method dynamically just as an argument to . (dot) - even if you use a macro to specify that argument.

A way to work around that is to include the . in your macro:

 (defmacro m1 [x y] `(. ~x (~y)))
 (m1 "a" toUppercase)
 user> "A"

Note that you need to wrap parentheses around ~y to indicate you want to call a method instead of reading a field.