我正在编写一个带有管道生态系统的流功能,特别是管道并发,它基于操作库,允许我快速制作一些程序片段,我通过网络或服务器向服务器发出命令stdin / out shell命令然后回读响应。在这种情况下,它是星号,但可以推广为类似的东西。
我最初用管道写这个,但它不起作用。以下代码不起作用的原因是astPipe返回Pipe _ _ IO a
,而来自管道并发的i和o都返回Consumer/Producer _ IO ()
。我考虑让astPipe
收益Maybe ByteString
,然后让输出Consumer
消耗Maybe ByteString
,但这仍然无法解决Producer
的问题} return ()
。
我觉得我真的很接近一个解决方案,但我无法解决这个问题。您应该能够在此文件上运行堆栈以进行复制。
#!/usr/bin/env stack
-- stack --resolver lts-6.20 runghc --package pipes --package pipes-concurrency --package operational --package process-streaming
{-# LANGUAGE OverloadedStrings, LambdaCase #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE GADTs #-}
module West.Asterisk where
import System.Process.Streaming as PS
import Control.Monad.Operational as Op
import Pipes as P
import Pipes.Concurrent as PC;
import qualified Data.ByteString.Char8 as B
import Control.Concurrent.Async
import GHC.IO.Exception (ExitCode)
data Version = Version String
data Channels = Channels
data AsteriskInstruction a where
Login :: AsteriskInstruction (Maybe Version)
CoreShowChannels :: AsteriskInstruction (Maybe Channels)
type Asterisk a = Program AsteriskInstruction a
runAsterisk :: forall a. Asterisk a -> IO a
runAsterisk m =
let
runAsterisk' :: Producer B.ByteString {- TODO Response -} IO () -> Consumer B.ByteString IO () -> Asterisk a -> IO a
runAsterisk' i o m' = runEffect $ i >-> astPipe m' >-> o
where
astPipe :: Asterisk a -> Pipe B.ByteString B.ByteString IO a
astPipe k =
case Op.view m' of
Return a -> return a
Login :>>= k -> do
yield logincmd
resp <- await -- :: Response
let v = undefined resp :: Maybe Version
astPipe (k v)
CoreShowChannels :>>= k -> do
yield coreshowchannelscmd
resp <- await
let c = undefined resp :: Maybe Channels
astPipe (k c)
in do
withSpawn unbounded $ \(out1, in1) -> do
async $ asteriskManager (fromInput in1) (toOutput out1)
runAsterisk' (fromInput in1) (toOutput out1) m
asteriskManager :: Producer B.ByteString IO () -> Consumer B.ByteString IO () -> IO ExitCode
asteriskManager prod cons = do
let ssh = shell "nc someserver 5038"
execute (piped ssh) (foldOut (withConsumer cons) *> feedProducer prod *> exitCode)
logincmd, coreshowchannelscmd :: B.ByteString
logincmd = "action: login\nusername: username\nsecret: pass\nevents: off\n\n"
coreshowchannelscmd = "action: coreshowchannels\n\n"
错误:
Blah.hs:38:45:
Couldn't match type ‘a’ with ‘()’
‘a’ is a rigid type variable bound by
the type signature for runAsterisk :: Asterisk a -> IO a
at Blah.hs:33:23
Expected type: Proxy () B.ByteString () B.ByteString IO ()
Actual type: Pipe B.ByteString B.ByteString IO a
Relevant bindings include
astPipe :: Asterisk a -> Pipe B.ByteString B.ByteString IO a
(bound at Blah.hs:41:9)
m' :: Asterisk a (bound at Blah.hs:38:22)
runAsterisk' :: Producer B.ByteString IO ()
-> Consumer B.ByteString IO () -> Asterisk a -> IO a
(bound at Blah.hs:38:5)
m :: Asterisk a (bound at Blah.hs:34:13)
runAsterisk :: Asterisk a -> IO a (bound at Blah.hs:34:1)
In the second argument of ‘(>->)’, namely ‘astPipe m'’
In the first argument of ‘(>->)’, namely ‘i >-> astPipe m'’
答案 0 :(得分:2)
Producer
的 Consumer
和()
可以自行停止。 Producer
和Consumer
对其返回类型的多态性永远不会自行停止。
要统一您的案例中的返回类型,请使用Either
将每个类型放在fmap
的不同分支中。
runAsterisk' :: Producer B.ByteString IO ()
-> Consumer B.ByteString IO ()
-> Asterisk a
-> IO (Either () a)
runAsterisk' i o m' = runEffect $ fmap Left i >-> fmap Right (astPipe m') >-> fmap Left o
Either
上的模式匹配将揭示哪个组件停止了管道。
此外,您可以使用drain
将Consumer a IO ()
转换为永不停止的消费者:
neverStop :: Consumer a IO () -> Consumer a IO r
neverStop consumer = consumer *> drain
原始消费者停止后收到的所有输入都将被丢弃。