2013-02-27 4 views
2

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

(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 вызывает ошибки. Как я могу это исправить?

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

ответ

5

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

Сделав шаг назад и глядя на то, что ваша общая цель, я думаю, вы должны переместить name-w-prefix определение вне синтаксиса кавычки, а затем использовать синтаксис-бежать, чтобы назвать его. В противном случае вы получите еще больше ошибок, потому что для 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 тела в соответствии с тем, что я говорил выше.

+0

Спасибо за объяснение! Не знал этого .. но я сделал это, и теперь он жалуется на символ 'x' в определении функции. – noncom

+0

См. Edit. Я думаю, вы немного смущены тем, что происходит внутри и вне цитат синтаксиса. – Alex

+0

Да, похоже! Например, я думал, что, поскольку все дело в AST, я мог бы возвратиться как '~ (expr)' от функции, и он заменит вызывающего, как будто он явно написан здесь. Но выглядит так: '' 'работает по-другому ... Спасибо за совет' ~ ''->' # 'также! Теперь я исправил все макросы, и все работает отлично! – noncom

Смежные вопросы