Haskell 管道 - 获取管道中最后一个代理的 return 值

Haskell Pipes - get return value of last Proxy in pipeline

假设我在 Haskell 管道中有两个 Proxy。它们代表外部系统进程。

produce :: MonadIO m => Producer ByteString m ExitCode
consume :: MonadIO m => Consumer ByteString m ExitCode

所以我将它们连接到一个 Effect 中,像这样:

effect :: Effect m ExitCode
effect = produce >-> consume

这个 Effect 将给我第一个 Proxy 终止的 ExitCode。通常这将是 produce,而不是 consume。即使它没有先终止,获取 consume 的 return 值的惯用 Pipes 方法是什么?

到目前为止,我认为如果不进行某种令人讨厌的带内信号发送是不可能的,因此 consume 知道流已完成。最后一个代理知道关闭的唯一方法是从 await 中获取一些东西,这样我就可以向它发送一个空的 ByteString 来表示流已完成。只是感觉不对。我现在得到的是一个单独的 MVar,它可以提供退出值,但我认为必须有一种更惯用的方法来做到这一点。

没有带内信号,如果 Producer return 首先出现,Consumer 将永远不可能有 "return value"。如果生产者 returning 意味着 Consumer 必须被阻塞等待请求的值。 Consumer 将永远不会再 运行,因此永远不会有机会 return,直到 Consumer 获得具有请求值的带内信号。

仅仅因为信号是带内的并不意味着它需要是 "icky"。我们可以将可能 return 的 Producer 转换为我们知道不 return 的 Producer (它的 return 类型是 forall r' . r')捕获 return 并将其转发到下游。我们这样做 forever 以防另一个请求返回上游。

returnDownstream :: Monad m => Proxy a' a b' b m r -> Proxy a' a b' (Either r b) m r'
returnDownstream = (forever . respond . Left =<<) . (respond . Right <\)

Consumer 结束时,您需要明确处理值被 requested 时要做什么,而不是得到响应(在 Right 中),您得到 return 上游生产者的值(在 Left 中)。

谢谢。我想出的是

produce :: MonadIO m => Producer (Either ExitCode ByteString) m r
consume :: MonadIO m => Consumer (Either ExitCode ByteString) m (Maybe ExitCode, ExitCode)

因此,当 Effect 运行时,如果下游进程终止,我将得到一个 (Nothing, code),或者如果上游进程首先终止,我将得到一个 (Just code1, code2)。 (如果下游先终止,那么上游进程除了终止它就没有其他事情可做了,所以提供退出代码没有任何意义。)