clojure - 不能在 core.async 的 go block 中使用 for 循环?

标签 clojure core.async

我是 clojure core.async 库的新手,我正试图通过实验来理解它。

但是当我尝试时:

(let [i (async/chan)] (async/go (doall (for [r [1 2 3]] (async/>! i r)))))

它给了我一个非常奇怪的异常(exception):
CompilerException java.lang.IllegalArgumentException: No method in multimethod '-item-to-ssa' for dispatch value: :fn

我尝试了另一个代码:
(let [i (async/chan)] (async/go (doseq [r [1 2 3]] (async/>! i r))))

它根本没有编译器异常。

我完全糊涂了。发生了什么事?

最佳答案

因此 Clojure go-block 在函数边界处停止翻译,原因有很多,但最大的原因是简单。这在构建惰性序列时最常见:

(go (lazy-seq (<! c)))

被编译成这样的东西:
(go (clojure.lang.LazySeq. (fn [] (<! c))))

现在让我们考虑一下这个真正的快速......这应该返回什么?假设您可能想要的是一个惰性序列,其中包含取自 c 的值,但是 <!需要将函数的剩余代码翻译成回调,但 LazySeq 期望函数是同步的。确实没有办法绕过这个限制。

所以回到你的问题,如果你宏扩展 for你会看到它实际上并没有循环,而是扩展成一堆最终调用 lazy-seq 的代码。所以 parking 操作在体内不起作用。 doseq (和 dotimes )但是由 loop 支持/recur所以这些都可以正常工作。

在其他一些地方,这可能会让你绊倒with-bindings作为一个例子。基本上,如果宏将您的 core.async parking 操作粘贴到嵌套函数中,您将收到此错误。

我的建议是让你的 go block 的主体尽可能简单。写纯函数,然后把 go blocks 的 body 当作做 IO 的地方。

- - - - - - 编辑 - - - - - - -

通过在函数边界处停止转换,我的意思是:go block 获取它的主体并将其转换为状态机。每次调用<! >!alts! (和其他一些)被认为是状态机转换, block 的执行可以暂停。在这些点中的每一个点,机器都会变成回调并附加到 channel 。当这个宏到达 fn形成它停止翻译。所以你只能调用<!从 go block 内部,而不是代码块内的函数内部。

这是 core.async 的魔力的一部分。如果没有 go 宏,core.async 代码在其他语言中看起来很像回调 hell 。

关于clojure - 不能在 core.async 的 go block 中使用 for 循环?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26040928/

相关文章:

multithreading - 在 core.async 中阻塞与线程

clojure - core.async channel 上的传感器

clojure - 绑定(bind)函数如何与 core.async 一起使用?

clojure - 具体化,ToString

clojure - 如何最好地关闭Clojure Core.Async进程管道

clojure - 使用core.async channel 在Clojure中使用http-kit的发布结果是否理智?

clojure - 如何清理 clojure core.async channel ?

clojure - 如何提高我的 clojure eratosthenes 算法的性能?

asynchronous - Clojure 的 core.async 是否类似于 Jane Street 的 OCaml Core Async?

map - 为什么 clojure 的 map 在 println 中表现得那样?