Динамическая форма let как часть reify в рамках макроса

Хорошо, давайте попробуем разобраться: моя конечная цель — предоставить пользователям макрос в виде API, который будет выглядеть так:

(defscript [a b]
  (println a))

Результатом должен быть экземпляр протокола Script, который выглядит так:

(defprotocol Script
  (run [this model]))

Идея состоит в том, что первый аргумент defscript — это список символов, которые необходимо связать с соответствующими ключами в model:

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

Я не могу придумать никакого кода, который мог бы эффективно производить такой эффект, так как я постоянно натыкаюсь на стену, пытаясь использовать параметр model, так как во время раскрытия макроса это просто символ:

(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- отлично работает, если вызывается напрямую:

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

но это не работает при использовании в defscript, так как model не может быть правильно расширено:

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

Как я могу обойти этот момент и склеить кусочки вместе?


person skuro    schedule 27.10.2011    source источник
comment
То, что вы описываете, отдаленно похоже на функции, представленные в clojure.template.   -  person Alex Taggart    schedule 28.10.2011


Ответы (1)


Кажется, что в основном ваш вектор аргумента является ярлыком для деструктурирующей привязки:

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

Таким образом, модель деструктурируется во время выполнения, как и должно быть.

person Joost Diepenmaat    schedule 28.10.2011