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/<! mail-box)] (recur (f v)))) mail-box)) (def ! a/put!) ;erlang 的操做符
能够看到,上面的 actor 函数能够将一个 core.async 通道封装成一个主动单元,其中拥有本身的事件循环,不断地在 mail-box 上等待新的消息。数据结构
下面咱们定义一个简单的调试 actor:异步
(def debug-actor (actor (fn debug[x] (prn x) debug))) (! debug-actor "Hello, world!") ;;输出 Hello, world
这最简单的 actor 固然尚未支持分布式编程,但使用这个模型,咱们将程序的组件变成了主动的单元,从而在通道的基础上提供了另外一种抽象。async
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
函数将内外两个通道连起来就能够了:oop
(defn actor-xf [xf out-ch] (let [ch (a/chan (a/dropping-buffer 32) xf)] (a/pipe ch out-ch) ch))