如何编写clojure宏来简化vertx事件总线通信



您好。。我使用vertx,它非常令人愉快,但对于事件总线通信,它需要很多样板,甚至最糟糕的是,如果我需要更改一些函数名称,我需要在4或5个不同的地方更改它。。

我正在使用一个抽象来将vertx回调转换为通道。。像这个

;;helper
(defn send [add msg]
   (let [ch (chan 1)]
      (eb/send add msg #(put! ch %))))
;;I wrap the event bus sender in a function for make a bit cleaner the code
(defn eb-get-cached-view [name id]
   (send "render:get-cached-view" [name id])) ;;return chan[response]
(eb-get-cached-view "something" "here");;and finally I use it
;;in other verticle, I write the funcion
(defn get-cached-view [name id]
   (...))
;;and enable an event bus listener pointing to that function
;; basically I receive a vector, pass it to my function, which return a channel, then I reply to the message with the response
(eb/on-message "render:get-cache-view" #(go (eb/reply (<! (apply get-cached-view %)))))

正如你所看到的,这是一个很大的样板,我担心每次更改函数名时,我都会错过在某个时候更改它,并且我的代码失败,消息上的参数我遵循使用我的命名空间的惯例,后面跟着":"和函数名

我在想,像这样的东西可能会很好

(defbus blah
        [name]
         (str "hi! " name))

该宏构建函数blah,并为具有"render"命名空间的消息创建总线侦听器(我可以访问宏中的命名空间吗?这是一种好方法吗?

(eb/on-message "namespace:blah" #(go (eb/reply (<!(apply get-cached-view %))))) ;;apply is necessary because the messages are received inside a vctor

以及用于发送消息的宏

(<eb namespace:blah "john Smith") ;; could be a key like :namespace:blah too

而不是翻译成类似的东西

(defn namespace:blah [message]
   (let [ch (chan 1)]
      (eb/send "namespace:blah" message #(put! ch %)))) 
(namespace:blah "john Smith")

有了这些宏,我将避免样板,更改promise的回调,或者避免函数名称不一致。。。

这是一个好方法吗?可能是写这些宏,还是我忽略了某些点?。。我对clojure有点陌生,我不知道这是否可能,也不知道为什么它还不存在。。。

我很感激任何帮助,也许可以更正我的代码(我用心写代码)

----------编辑1------------------------------------------------------------

我写了这个宏(感谢亚瑟)。。。它比最终的宏简单一点,只用于演示

(defmacro defbus [bus-name args code]
   `(let [fun# (fn ~args ~code)]
       (println (str *ns* ":" ~bus-name))
       (eb/on-message (str *ns* ":" ~bus-name) (fn [arg#] (apply fun# arg#)))))

我认为比宏观扩张还不错。。。但当我尝试使用它时,我会得到

(defbus blah [a b] (str "just " a " " b))
java.lang.RuntimeException: Unable to resolve symbol: blah in this context

似乎我可以直接使用blah,但一个simbol或字符串。。。我是不是错过了什么?(我是宏的新手,所有的例子都与我需要的非常不同)谢谢!。。。

您可以在中的任何位置以名称*ns*访问名称空间窗体。在这种情况下,如果宏使用进行调用的名称空间而不是定义辅助函数或宏的名称空间来定义事件总线,则会很方便。

user> (defmacro defbus [bus-name]
        `(eb/on-message (str *ns* ":" ~bus-name) :dostuff))
#'user/defbus
user> (macroexpand-1 '(defbus foo))
(eb/on-message (clojure.core/str clojure.core/*ns* ":" foo) :dostuff)

该宏将返回对消息的调用的代码,然后在调用者命名空间中对其进行评估,其中ns绑定到该命名空间的,我认为这就是您想要的。

最新更新