clojure 中使用 actor

背景

Actor 模型 是让 Earlang 声名卓著的关键特性。它是 Erlang 平台实现分布式编程的关键内容,在 Clojure 语言设计时, Rich Hickey 考虑过在 Clojure 语言中是否实现 Actor,他最终认为:这仅仅是适合于分布式编程的一种特征,如果成为语言的本质,将限制 Clojure 成为一种服务器领域语言,因此他决定使用其他更简单直接的异步通信模型。但他也没有排除未来在 Clojure 中引入 Actor 的可能。

简单实现

随着 core.async 库的推出和成熟,我们实际上已经可以用 core.async 来实现一个最简单的 Actor 模型:

(require '[clojure.core.async :as a])

(defn actor [f]
(let [mail-box (a/chan (a/dropping-buffer 32))]
(a/go-loop [f f]
(when-let [v (a/
可以看到,上面的 actor 函数可以将一个 core.async 通道封装成一个主动单元,其中拥有自己的事件循环,不断地在 mail-box 上等待新的消息。

下面我们定义一个简单的调试 actor:

(def debug-actor (actor (fn debug<input type="checkbox" checked="" disabled=""> (prn x) debug)))

(! debug-actor "Hello, world!")
;;输出 Hello, world
这最简单的 actor 当然还没有支持分布式编程,但使用这个模型,我们将程序的组件变成了主动的单元,从而在通道的基础上提供了另一种抽象。

与 Transducers 的关系

Clojure 1.7 开始引入的 transducer 被广泛使用,它可以使用在任何连续的数据结构上,例如序列,以及 core.async 的通道,如果我们已经定义好了一个 transducer 或者用它定义的操作 (xf),是否可以用于 Actor 呢?

(defn actor-xf [xf out-actor]
(let [f-out (fn
([b] b)
([b itm] (a/put! b itm) b))]
(fn f-actor
([v] (f-actor out-actor v))
([acc v]
(let [acc ((xf f-out) acc v)]
(partial f-actor acc))))))
这个函数可以用定义好的 xf 生成一个 actor,它将对消息处理后将转化后的消息送往 out-actor。例如:

(def inc-actor (actor (actor-xf (map inc) debug-actor)))
(! inc-actor 6)
;;输出7
(def complex-actor (actor (actor-xf (mapcat # (repeatedly % vector)) debug-actor)))
(! complex-actor 2)
;;[]
;;[]
其实 core.async 库中已经实现了类似的功能,就是 chan 函数本身!我们不过需要用 pipe 函数将内外两个通道连起来就可以了:

(defn actor-xf
[xf out-ch]
(let [ch (a/chan (a/dropping-buffer 32) xf)]
(a/pipe ch out-ch)
ch))

关键字:clojure, erlang


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部