Dynamic let form as part of reify within a macro

210 views Asked by At

Ok, let's try to get this straight: my final intent is to provide a macro as an API to users which will look like:

(defscript [a b]
  (println a))

The result has to be an instance of a Script protocol, which looks like:

(defprotocol Script
  (run [this model]))

The idea being that the first argument to defscript is a list of symbols that needs to be bound to correspondent keys in the model:

(.run (defscript [a b] (println a)) {:a 1}) ;; yields 1

I can't come up with any code that can effectively produce such effect, as I'm constantly hitting a wall when trying to use the model parameter, since at macro expansion time it's just a symbol:

(defmacro invoke-
  [params model body]
  (let [p (flatten (map (fn [x] [x (model (keyword x))]) params))]
    `(let [~@p]
       ~body)))

(defmacro defscript
  [params & body]
  `(reify Script
    (run [~'this ~'model]
      (invoke- ~params ~'model ~@body))))

invoke- works fine if called directly:

(invoke- [a] {:a 1} (println a)) ;; prints 1

but it doesn't work when used within defscript as model can't be expanded correctly:

(.run (defscript [a] (println a)) {:a 1}) ;; prints nil

How can I get past this point and glue the pieces together?

1

There are 1 answers

0
Joost Diepenmaat On BEST ANSWER

It seems that basically, your argument vector is a shortcut for a destructuring binding:

(defscript [a b] body)  -> (reify Script (run [this {:keys [a b]}] body))

That way, model is destructured at run time, as it should be.