我正在浏览他们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”]。
答案 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)))
注意添加〜'在ch
和ex
上添加了〜data
和default-exchange-name
。另请注意,从for
到doseq
的更改为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"))
包含符号ch
和ex
,它们必须在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
本身就是宏)。
如果你需要在多个地方做类似的事情,只需定义一个功能。如果您有一些上下文(例如ch
和ex
),您不必重复或需要逃避其词法范围,请创建一个闭包。