Clojure 不能';不用于core.async的go块中的循环?

Clojure 不能';不用于core.async的go块中的循环?,clojure,core.async,Clojure,Core.async,我是clojure core.async库的新手,我试图通过实验来理解它 但当我尝试时: (let [i (async/chan)] (async/go (doall (for [r [1 2 3]] (async/>! i r))))) 这给了我一个非常奇怪的例外: CompilerException java.lang.IllegalArgumentException: No method in multimethod '-item-to-ssa' for dispatch valu

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

但当我尝试时:

(let [i (async/chan)] (async/go (doall (for [r [1 2 3]] (async/>! i r)))))
这给了我一个非常奇怪的例外:

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块在函数边界处停止转换,原因很多,但最大的原因是简单。这在构造惰性seq时最常见:

(go (lazy-seq (<! c)))
(go(lazy seq)(
被编译成如下内容:

(go (clojure.lang.LazySeq. (fn [] (<! c))))
(go(clojure.lang.LazySeq.(fn[](
现在让我们快速思考一下这个问题……这个函数应该返回什么?假设您可能想要的是一个包含从c获取的值的惰性seq,但是
需要将函数的剩余代码转换为回调,但是lazysek希望函数是同步的。真的没有办法绕过这个限制

回到你的问题,如果你对
进行宏展开,你会发现它实际上并没有循环,相反,它会展开成一堆代码,最终调用
惰性seq
,因此驻车操作在体内不起作用。
doseq
(和
dotimes
)但是,它们有
循环
/
重复
作为后盾,因此它们可以很好地工作

在其他一些地方,这可能会绊倒您,绑定就是一个例子。基本上,如果宏将core.async parking操作粘贴到嵌套函数中,您将得到此错误

我的建议是让你的go块体尽可能简单,编写纯函数,然后把go块体当作IO的地方

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

通过在函数边界处停止转换,我的意思是:go块将其主体转换为状态机。每次调用
alts!
(以及其他一些调用)被认为是状态机转换,块的执行可以暂停。在这些点中的每一点,机器都变成回调并连接到通道。当此宏到达
fn
窗体时,它停止转换。因此,您只能从go块内部调用
,而不能从代码块内部的函数内部调用。


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

for
在clojure中不是一个循环,而是一个列表理解。记住这一点,你的
for
示例并不是调用副作用
函数的惯用方法。也许编译器消息可以/应该得到改进,但您的根本问题是,以这种方式使用
for
不会产生任何效果(clojure)感觉。
doseq
非常好。>!blocks,它等待有人从频道读取。尝试先设置读取部分,或者使用put!@edbond哇,这实际上是有效的。。但我更困惑的是,蒂莫西·鲍德里奇不是刚刚说async/go不能处理
async/go
block中的
fn
吗?请解释一下你做了什么你的意思是说,
停止函数边界处的转换
?添加到我的原始帖子中,因此core.async可以为:fn添加一个方法,并抛出一个更好的异常。这对初学者来说非常不友好。从注释中学习,
(让[i(async/chan)](async/go(doall(for[r[13]](async/put!ir))(async/go(println))(async/实际上是有效的,这与
fn
不能出现在
async/go
块中的想法不冲突吗?这是否意味着如果库(data.xml)想要一个序列,你就不需要了?