xud*_*fsd 9 clojure core.async
我是clojure core.async库的新手,我试图通过实验来理解它.
但当我尝试时:
(let [i (async/chan)] (async/go (doall (for [r [1 2 3]] (async/>! i r)))))
Run Code Online (Sandbox Code Playgroud)
它给了我一个非常奇怪的例外:
CompilerException java.lang.IllegalArgumentException: No method in multimethod '-item-to-ssa' for dispatch value: :fn
Run Code Online (Sandbox Code Playgroud)
我尝试了另一个代码:
(let [i (async/chan)] (async/go (doseq [r [1 2 3]] (async/>! i r))))
Run Code Online (Sandbox Code Playgroud)
它根本没有编译器异常.
我完全糊涂了.发生了什么事?
Tim*_*dge 18
所以Clojure go-block在功能边界停止翻译,原因很多,但最大的是简单性.这在构造一个懒惰的seq时最常见:
(go (lazy-seq (<! c)))
Run Code Online (Sandbox Code Playgroud)
获取编译成这样的东西:
(go (clojure.lang.LazySeq. (fn [] (<! c))))
Run Code Online (Sandbox Code Playgroud)
现在让我们快速思考一下这应该是什么呢?假设您可能想要的是一个包含从c获取的值的延迟seq,但是<!需要将函数的剩余代码转换为回调,但是LazySeq期望该函数是同步的.实际上没有办法解决这个限制.
所以回到你的问题,如果你宏观扩展for你会看到它实际上没有循环,而是扩展成一堆最终调用的代码,lazy-seq所以停车操作不能在机体内部工作.doseq(并且dotimes)然后由loop/ 支持recur,所以那些将完美地工作.
还有一些其他地方可能会让你with-bindings感到沮丧是一个例子.基本上,如果宏将您的core.async停放操作粘贴到嵌套函数中,您将收到此错误.
我的建议是尽可能简化你的块体.编写纯函数,然后将go块的主体视为IO的位置.
------------编辑-------------
通过在函数边界处停止转换,我的意思是:go块占用它的主体并将其转换为状态机.每次调用<! >!或alts!(和其他一些)被认为是状态机转换,其中块的执行可以暂停.在每个点上,机器变成回调并连接到通道.当此宏到达fn表单时,它将停止转换.因此,您只能<!从go块内部进行调用,而不是在代码块内的函数内部进行调用.
这是core.async魔力的一部分.没有go宏,core.async代码看起来很像其他语言中的回调地狱.
| 归档时间: |
|
| 查看次数: |
985 次 |
| 最近记录: |