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
Both the operator and the operands (if any) are evaluated, from left to right. The result of the evaluation of the operator is cast to IFn (the interface representing Clojure functions), and invoke() is called on it, passing the evaluated arguments. The return value of invoke() is the value of the call expression.
Clojure has a programmatic macro system which allows the compiler to be extended by user code. Macros can be used to define syntactic constructs which would require primitives or built-in support in other languages. Many core constructs of Clojure are not, in fact, primitives, but are normal macros.
Clojure (/ˈkloʊʒər/, like closure) is a dynamic and functional dialect of the Lisp programming language on the Java platform. Like other Lisp dialects, Clojure treats code as data and has a Lisp macro system.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With