It's not the same kind of non-understanding which I have in mind. I'm talking about a macro/DSL being able to not understand what is passed to it.
I think it's an important feature for the user to be able to get out of your DSL and back in regular Clojure.
In both Moustache and Enlive, I avoid to give a special meaning to lists (in Clojure you also have vectors, maps and sets), hence lists always denote user-code and are embedded as-is in the macro expansion. (If I really had to use lists for a DSL, I would check a list doesn't start with a special value (eg do
or unquote
(~) — thanks to MB's comment) before processing it).
That's why in Enlive you can easily add your own selectors step [:p (my-selector-step arg1 arg2)]
as long as (my-selector-step arg1 arg2)
evaluates to a correct value (here a state machine).
That's also how Moustache supports wrapping another Ring handler or custom route validation.
3 comments:
I use the following approach. It follows a bit Scheme quasiquote.
The implementation is basically a modified syntax-quote from
Clojure's compiler. The user can provide anything and verbatim
code is simply prefix with ~. As in a normal macro.
Eg:
(let [x 5]
(quasiquote (+ ~x 6))
will give
(+ 5 6)
Here is the implementation:
(defn- unquote?
"Tests whether the given form is of the form (unquote ...)."
[form]
(and (seq? form) (= (first form) `unquote)))
(defn- quasiquote*
"Worker for quasiquote macro. See docstring there. For use in macros."
[form]
(cond
(self-eval? form) form
(unquote? form) (second form)
(symbol? form) (list 'quote form)
(vector? form) (vec (map quasiquote* form))
(map? form) (apply hash-map (map quasiquote* (flatten-map form)))
(set? form) (apply hash-set (map quasiquote* form))
(seq? form) (list* `list (map quasiquote* form))
:else (list 'quote form)))
(defmacro quasiquote
"Quote the supplied form as quote does, but evaluate unquoted parts.
Example: (let [x 5] (quasiquote (+ ~x 6))) => (+ 5 6)"
[form]
(quasiquote* form))
Hi Meikel,
I have been bitten by code-walking (the previous Iteration of Enlive used unquote), I think I'll avoid it for some time (until Clojure compiler is rewritten in Clojure). But I agree 'unquote is maybe a better choice than 'do to denote user-code in my current approach.
I'm also not a big fan of code walking. In general it bears a lot of (subtle) problems. And I would feel much more comfortable if something like quasiquote was in core complementing quote and syntax-quote. But Rich is not very keen on including quasiquote...
I saw a lot of its usage in scsh, eg. the shell and regex DSLs. And it worked there quite nicely. But Scheme brings it as a language construct. That is much more robust than a handcrafted hack. :]
Post a Comment