为RabbitMQ扇形重构Clojure代码中的冗余

时间:2014-04-15 01:27:46

标签: macros clojure rabbitmq langohr

我正在浏览他们web-page上的RabbitMQ教程,并尝试重构他们提供的示例,以使它们更通用和可组合。我被困在第二个“blabbr”的例子上。这是我想要重构的函数:

(defn -main
  [& args]
  (let [conn  (rmq/connect)
        ch    (lch/open conn)
        ex    "nba.scores"
        users ["joe" "aaron" "bob"]]
    (le/declare ch ex "fanout" :durable false :auto-delete true)
    (doseq [u users]
      (start-consumer ch ex u))
    (lb/publish ch ex "" "BOS 101, NYK 89" :content-type "text/plain" :type "scores.update")
    (lb/publish ch ex "" "ORL 85, ALT 88"  :content-type "text/plain" :type "scores.update")
    (Thread/sleep 2000)
    (rmq/close ch)
    (rmq/close conn)))

我以为我可以创建一个宏并在函数中调用它,如下所示:

(defmacro wrap-publish [default-exchange-name content mType data]
  `(for [datum# data]
  (lb/publish ch ex default-exchange-name datum# :content-type  ~content :type ~mType)))

(defn -main
      [...]
... 
(wrap-publish default-exchange-name content-type mType data)
...

但是,当我在repl上自行测试wrap-publish宏时,我收到此错误:

java.lang.ClassCastException: clojure.lang.Var$Unbound cannot be cast to` com.novemberain.langohr.Channel basic.clj:89 langohr.basic/publish

似乎有一些全球性的事情不会让我束​​缚我的变种,但我不知道是什么。我跟着我的鼻子走向从堆叠痕迹向我投掷的source-code,并在那里遇到了死胡同。我只是不知道该怎么做。我是一个新的程序员,带着婴儿步骤进入异步和宏的世界。因此,我将不胜感激任何建议,这些建议不仅可以帮助我实现我的目标,还可以提供一些能够告知我基本技能的一般见解,并让我知道我是否采取了正确的方法。我正在使用langohr依赖[com.novemberain / langohr“2.9.0”]。

1 个答案:

答案 0 :(得分:3)

关于宏写的

您的宏定义中似乎缺少一些非引号。我没有使用该库,但您没有提供SSCCE,所以我建议不进行测试。

你的宏应该是

(defmacro wrap-publish [default-exchange-name content mType data]
  `(doseq [datum# ~data]
     (lb/publish ~'ch ~'ex ~default-exchange-name datum# :content-type  ~content :type ~mType)))

注意添加〜'在chex上添加了〜datadefault-exchange-name。另请注意,从fordoseq的更改为for是懒惰的。

你会像这样使用

...
(let [...
      ch ...
      ex ...
      ...] ; end let bindings
...
(wrap-publish "" "text/plain" "scores.update" ["BOS 101, NYK 89", "ORL 85, ALT 88"]))
...

因为这会产生代码

(macroexpand-1 '(wrap-publish "" "text/plain" "scores.update" ["BOS 101, NYK 89", "ORL 85, ALT 88"]))
;=> (clojure.core/doseq [datum__1237__auto__ ["BOS 101, NYK 89" "ORL 85, ALT 88"]] 
;     (lb/publish ch ex "" datum__1237__auto__ :content-type "text/plain" :type "scores.update"))

包含符号chex,它们必须在let绑定中可用。

关于所要求的建议

在这里编写宏是没有充分理由的。如果您正在寻找建议,请避免在Clojure的前6-12个月内完全编写宏。首先关注函数式编程技巧。在那之后,仍然尽可能避免编写宏!

这个宏生成的代码应该只是你编写的代码:

(doseq [d ["BOS 101, NYK 89" "ORL 85, ALT 88"]] 
   (lb/publish ch ex "" d :content-type "text/plain" :type "scores.update"))

而不是编写自己的宏(doseq本身就是宏)。

如果你需要在多个地方做类似的事情,只需定义一个功能。如果您有一些上下文(例如chex),您不必重复或需要逃避其词法范围,请创建一个闭包。