purescript-coroutines中`pullFrom`和`connect`的区别是什么?

时间:2017-03-10 19:01:27

标签: coroutine purescript

我不确定我理解purescript-coroutines中有connectpullFrom功能的原因,以及何时使用它们。看看这些类型,似乎他们已经习惯了改变沟通的方向" (我不确定这是不是正确的思考方式)。

pullFrom :: forall o m a. MonadRec m => Consumer o m a -> Producer o m a -> Process m a

connect :: forall o f m a. (MonadRec m, Parallel f m) => Producer o m a -> Consumer o m a -> Process m a

所以如果我有消费者和制片人......

consumer :: forall e a. (Show a) => Consumer a (Eff (console :: CONSOLE | e)) Unit
consumer = forever do
  s <- await
  lift (log $ show s)

numberProducer :: forall m. (Monad m) => Producer Int m Unit
numberProducer = go 0
  where
  go i = do emit i
            go (i + 1)

对我来说,消费者可以从生产者那里取出来是有意义的,如果我运行它,我可以看到数字显示......

main = do
  runProcess (pullFrom consumer numberProducer)

但如果我connect生产者是消费者,它似乎什么都不做。我假设当你将生产者连接到消费者时,信号传递的方向与pullFrom相反,但我不确定这个想法是否正确。

main = do
  runProcess (connect producer consumer)

1 个答案:

答案 0 :(得分:4)

嗯,这里有一个有趣的小惊喜......我会在一分钟内来到这里。

pullFrom被引入,以便消费者在形成流程时“负责” - 只要消费者处于打开状态(等待输入),该流程就会存在。

只要生产者或消费者处于打开状态,

connect就会运行,并且只有当他们都完成时才会终止。

为实现此目的,connect具有Parallel类约束,因为创建的进程依赖于使用者和生成者 - pullFrom不需要这样,因为该进程仅依赖于consumer

这就是“有趣”惊喜的来源 - 让我困惑了一分钟。 Eff不是Parallel ...那么您的代码如何运作?这是因为它推断main的这种类型:

main :: forall t. (Parallel t (Eff (console :: CONSOLE))) => Eff (console :: CONSOLE) Unit

因此在程序运行时没有任何反应,因为在JS中,main期望为Parallel约束传递字典,然后评估Eff。生成的对main的调用仅为Main.main();,因此它实际上从未评估Eff,因为它需要Main.main(impossibleParallelDictionary)();

尝试将此类型添加到main

main :: Eff (console :: CONSOLE) Unit

你会发现它不再打字了。

虽然您可以使用Aff,但Aff connectpullFrom之间的区别对于此示例无法区分:

import Prelude

import Control.Coroutine (Consumer, Producer, await, connect, emit, runProcess)
import Control.Monad.Aff (Aff, launchAff)
import Control.Monad.Aff.Console (CONSOLE, log)
import Control.Monad.Eff (Eff)
import Control.Monad.Eff.Exception (EXCEPTION)
import Control.Monad.Rec.Class (forever)
import Control.Monad.Trans.Class (lift)

consumer :: forall e a. (Show a) => Consumer a (Aff (console :: CONSOLE | e)) Unit
consumer = forever do
  s <- await
  lift (log $ show s)

numberProducer :: forall m. (Monad m) => Producer Int m Unit
numberProducer = go 0
  where
  go i = do emit i
            go (i + 1)

main :: Eff (err :: EXCEPTION, console :: CONSOLE) Unit
main = void $ launchAff $ runProcess (connect numberProducer consumer)

如果我们稍微修改一下这个例子,我们可以看到差异的例子:

import Prelude

import Control.Coroutine (Consumer, Producer, await, emit, connect, runProcess)
import Control.Monad.Aff (Aff, launchAff, later')
import Control.Monad.Aff.Console (CONSOLE, log)
import Control.Monad.Eff (Eff)
import Control.Monad.Eff.Exception (EXCEPTION)
import Control.Monad.Trans.Class (lift)

consumer :: forall e a. (Show a) => Consumer a (Aff (console :: CONSOLE | e)) Unit
consumer = do
  s <- await
  lift (log $ show s)

numberProducer :: forall eff. Producer Int (Aff eff) Unit
numberProducer = do
  emit 0
  lift $ later' 10000 $ pure unit

main :: Eff (err :: EXCEPTION, console :: CONSOLE) Unit
main = void $ launchAff $ runProcess (connect numberProducer consumer)

这样,程序将打印0,等待10秒,然后退出。如果您为connect numberProducer consumer切换consumer `pullFrom` numberProducer,程序将打印0并立即退出。