Rich Hickey 的 Strange Loop transducer presentation 告诉我们在 Clojure 1.6 中有两种
map
的实现,一种用于 clojure.core
中的序列,一种用于 core.async
中的通道。
现在我们知道在 1.7 中我们有换能器,当给定一个函数而不是一个集合时,从高阶函数如
foldr
和 reduce
返回一个 map
(filter
) 函数。
我试图阐明和失败的是为什么
core.async
函数不能返回序列,或者像 Seq
一样。我觉得“接口”(协议)不同,但我看不出如何。
当然,如果您要从频道中取出第一项,那么您可以将其表示为从序列中取出第一项吗?
我的问题是:
core.async
是否已经按照序列实现了它的功能?
是的,从某种意义上说,他们本来可以。如果你忽略 go 块(暂时让我们这样做),那么像下面这样的东西真的没有错:
(defn chan-seq [ch]
(when-some [v (<!! c)]
(cons v (lazy-seq (chan-seq ch)))))
但是请注意这里的
<!!
电话。这称为“take blocking”:此函数内部有一些 promises 和锁,它们将导致当前正在执行的线程暂停,直到通道上的值可用为止。因此,如果您不介意让 Java 线程坐在那里什么也不做,那么这会很好地工作。
go blocks背后的想法是让逻辑过程更便宜;为了实现这一点,go 块将块的主体重写为一系列附加到通道的回调,以便在内部对 go 块内的
<!
的调用变成这样的东西 (take! c k)
其中 k
是对 go 块其余部分的回调。
现在如果我们有真正的延续,或者如果 JVM 支持轻量级线程,那么是的,我们可以结合 go-blocks 和阻塞 takes。但这目前涉及深度字节码重写(就像 Pulsar/Quasar 项目所做的那样)或一些非标准的 JVM 功能。在创建 core.async 时排除了这两个选项,取而代之的是更易于实现(并且希望更易于推理)的本地 go 块转换。
稍微调整一下,使第一个项目的拍摄也变得懒惰。
(defn chan-seq [ch]
(lazy-seq
(if-some [v (<!! ch)]
(cons v (chan-seq ch))
nil)))
> Blockquote