Clojure - запуск макроса не работает

Я создал макрос, который создает именованный dispatcher с 3 ассоциированными функциями get-dispatcher, set-dispatcher и call-dispatcher для работы с диспетчером (они получают функцию диспетчеризации, добавляют ее или вызывают). Все работает отлично! Однако теперь я хочу автоматизировать создание имен связанных функций, поэтому я помещаю все эти внутренние компоненты макроса в let, который определяет эту простую конструктивную функцию. Обратите внимание, что в приведенном ниже коде с помощью этой автоматизации создается только имя функции get-. Создание имени set- и call- до сих пор имеет этот ручной запах.

(defmacro create-dispatcher [name]
  ;creates a set of dispatching functions tagged

  `(do
    ;define dispatcher
    (def ~(symbol name) ~(atom {}))

    (let
      [name-w-prefix (fn [x] (~(symbol (str x "-" name))))]
        ; -- define getter
        (defn (name-w-prefix "get")
          "get-dispatcher [tag]: get a dispatcher fn by tag"
          (~'[] (println "no tag is provided for '" ~(str name) "' dispatcher"))
          (~'[tag]
            (do
              (println "dispatcher '" ~(str name) "' called with '" ~'tag "' tag")
              ; return the tagged dispatcher
              ( (keyword ~'tag) @~(symbol name) )))

        )
        ; -- define caller
        (defn ~(symbol (str "call-" name))
          "get-dispatcher [tag & args]: call a dispatcher fn by tag and apply to the args"
          ~'[tag & args]
          (apply (~(symbol (str "get-" name)) ~'tag) ~'args)
          )
        ; -- define setter
        (defn ~(symbol (str "set-" name))
          ~'[tag fn]
          "add-dispatcher [tag fn]: add a dispatcher fn associated with the tag"
          (swap! ~(symbol name) assoc (keyword ~'tag) ~'fn)
          )
     )

    ; -- report
    (println "created dispatcher set for '" ~(str name) "' ok!")
    ))

Однако есть проблема. name-w-prefix в привязке оператора let вызывает ошибки. Как я могу это исправить?

(также приветствуются любые советы по улучшению, так как я новичок, и это почти первое, что я написал на Clojure)


person noncom    schedule 27.02.2013    source источник


Ответы (1)


Все символы в макросе разрешаются в текущем пространстве имен и, как ожидается, будут оцениваться как переменная. Вы можете заключить в кавычки символ name-w-prefix, но это может привести к конфликту с символами, переданными в макрос во время раскрытия макроса. Итак, Clojure предоставляет специальный синтаксис для использования в формах с синтаксическими кавычками для генерации символов - просто добавьте # в конец символа, и Clojure будет рассматривать его как автоматически сгенерированный символ в кавычках. В этом случае замените вхождения name-w-prefix на name-w-prefix#, и все будет в порядке.

Сделав шаг назад и посмотрев, какова ваша общая цель, я думаю, вам следует переместить определение name-w-prefix за синтаксические кавычки, а затем использовать синтаксис-escape для его вызова. В противном случае вы получите еще больше ошибок, потому что defn требуется символ, поэтому после раскрытия макроса должна получиться форма defn, которая имеет символ в качестве второго элемента. Что-то вроде:

(defmacro create-dispatcher [name]
  (let [name-w-prefix #(symbol (str % "-" name))]
    `(do
       (def ~(symbol name) (atom {}))
       (defn ~(name-w-prefix "get")
         ([] (println "no tag provided"))
         ([tag#] (println "called with tag" tag#))))))

Обратите внимание, что я изменил ~'[tag] на [tag#] в теле defn в соответствии с тем, о чем я говорил выше.

person Alex    schedule 27.02.2013
comment
Спасибо за объяснение! Не знал ... но я сделал это, и теперь он жалуется на символ x в определении функции .. - person noncom; 28.02.2013
comment
См. Редактировать. Я думаю, вы немного запутались в том, что находится внутри и вне синтаксических кавычек. - person Alex; 28.02.2013
comment
Да, похоже! Например, я подумал, что, поскольку все дело в AST, я мог бы вернуть как ~(expr) из функции, и он заменит вызывающего, как если бы он был здесь явно написан. Но похоже, что ~ работает по-другому ... Спасибо за совет ~' - ›#! Теперь я исправил все макрос, и он отлично работает! - person noncom; 28.02.2013