Clojure 不能';不用于core.async的go块中的循环?
我是clojure core.async库的新手,我试图通过实验来理解它 但当我尝试时: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
(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)想要一个序列,你就不需要了?