1

我正在浏览他们网页上的 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)
...

wrap-publish但是,当我在 repl 上单独测试宏时,出现此错误:

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

似乎有一些全球性的事情不会让我绑定我的变量,但我不知道是什么。我顺着鼻子找到了从堆栈跟踪中扔给我的源代码 ,并在那里遇到了死胡同。我只是不知道该怎么办。我是一名新程序员,正在逐步进入异步和宏的世界。因此,我将不胜感激任何建议,这些建议不仅可以帮助我实现目标,还可以提供一般见解,这些见解将告知我的基本技能,并让我知道我是否采取了正确的方法。我正在使用 langohr 依赖项 [com.novemberain/langohr "2.9.0"]。

4

1 回答 1

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)))

注意添加的 ~' on chandex和添加的 ~ on dataand default-exchange-name。另请注意,从更改fordoseqasfor是惰性的。

你会这样使用

...
(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是宏本身)。

如果您需要在多个地方做类似的事情,只需定义一个函数。如果你有一些你不想重复的上下文(例如chand )或者需要逃离它的词法范围,创建一个闭包。ex

于 2014-04-15T03:49:45.087 回答