避免go宏限制的可能解决方案:必须在(go ...)块内调用

时间:2013-10-23 10:16:34

标签: clojure core.async

使用(<! c)外部宏的可能解决方案也许可以通过宏及其宏扩展时间来完成:

这是我的例子:

(ns fourclojure.asynco
      (require [clojure.core.async :as async :refer :all]))

(defmacro runtime--fn [the-fn the-value]
  `(~the-fn ~the-value)
  )
(defmacro call-fn [ the-fn]
  `(runtime--fn ~the-fn (<! my-chan))
  )

(def my-chan (chan))

(defn  read-channel [the-fn]
  (go
  (loop []
    (call-fn the-fn)
    (recur)
    )
  ))

(defn paint []
  (put! my-chan "paint!")
  )

并测试它:

(read-channel print)
(repeatedly 50 paint)

我在嵌套go中尝试过这个解决方案并且也可以使用。但我不确定它是不是一条正确的道路

关于这个问题的原因与另一个问题Isn't core.async contrary to Clojure principles?有关,@ aeuhuea评论说“在我看来,这会妨碍简单性和可组合性。为什么这不是问题?”和@cgrand响应“go宏(它的位置)的限制也是一个特性:它强制执行有状态操作的源代码局部性。” 但强制你的代码本地化与“complect”不一样吗?

1 个答案:

答案 0 :(得分:6)

关于你问题的标题:

必须在go块中调用

>!,因为它是为了设计的。如果您对封锁状态机机制感兴趣,我强烈推荐Timothy Baldridges Youtube视频http://www.youtube.com/channel/UCLxWPHbkxjR-G-y6CVoEHOw

请注意,始终会阻止>!!<!!。我不知道你的代码的哪一部分应该提供一个“解决方案”,因为它无法在go块之外使用<!>!,但是会循环从单个通道调度的事件是常见的做法。这是读通道的修改版本

(defn do-channel [f ch]
  (go-loop []
    (when-let [v (<! ch)]
      (f v)
      (recur))))

把!异步,一种你通常不想要的效果。在你的例子中,要将字符串“paint”放入通道50次,我建议像这样的单行:

(do-channel println (to-chan (repeat 50 "print")))

以下是评论作为您编辑的答案: 通道不是设计用作可变数据结构的周期。它们有一个缓冲区,缓冲区可以被认为是一个可变队列。但是我们不使用通道在那里存储值,只是稍后再将它取出几行。 我们使用通道作为帮助构造,可以用于将两个或更多个不同的源代码执行在两个或更多个不同的位置。例如。这里的go-block在收到另一个go-block产生的值之前不会继续执行。 >!>!!帮助我们区分它们是用于线程阻塞上下文还是用于阻止生成的进程(阻止生成的进程)。

此外,请参阅此答案:Clojure - Why does execution hang when doing blocking insert into channel? (core.async)

您不应在go-block中使用>!!<!!,也不应在函数调用中透明或嵌套。 Rich Hickey本人在最近的错误报告(http://dev.clojure.org/jira/browse/ASYNC-29?focusedCommentId=32414&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-32414)中对此进行了评论。

查看>!的源代码,您会发现它只会引发异常。事实上,go将使用不同的源代码替换>!go产生一个由状态机控制的进程。根据您可能希望明确知道的上下文或将go块嵌套在宏或函数内部(如在您提供的代码示例中)。

关于David Nolens(swannodettes)助手:他们已经由Rich Hickey和Nolen自己实施到core.async库中。诺伦说自己在这次演讲中被取代了(http://www.youtube.com/watch?v=AhxcGGeh5ho)。请注意,自Nolens提交后,go-loop已经实现。