这是关于Haskell streaming
库的问题。
Stream (Of a) m r
是“从某些monad m
中的操作派生的单个Haskell值的流,并返回类型r
的值”。 Streaming.Prelude
定义了许多有用的函数,允许很好的流应用程序:
import qualified Streaming.Prelude as S
S.print $ do
S.yield "a"
S.yield "b"
S.yield "c"
tutorial适合入门。
现在,手头的特定问题是如何将此框架与需要仔细实例化和释放资源的monad一起使用。 streaming-with
包似乎是正确的候选者,它有一个函数
bracket :: MonadMask m => m a -> (a -> m c) -> (a -> m b) -> m b
获取(m a
),发布(a->m c
)并使用(a->m b
)资源。所有三个操作都封装在返回的m b
中。 withFile
是如何使用它的一个很好的例子:
withFile :: FilePath -> IOMode -> (Handle -> m r) -> m r
withFile fp md = bracket (liftIO (openFile fp md)) (liftIO . hClose)
获取和释放句柄很好地将用法Handle->m r
夹在中间。
但是:我绝对不知道这应该如何与Stream (Of a) m r
一起使用。我必须提供a->m b
并获得m b
。这应该如何连接,以便我获得Stream
?
要解决这个问题,让我们一起玩withFile
:
import System.IO
use :: Handle -> IO (S.Stream (Of String) IO ())
use = return . S.repeatM . hGetLine
main :: IO ()
main = do
str <- S.withFile "input.dat" ReadMode use
S.print str
但结果为hGetLine: illegal operation (handle is closed)
。这实际上是有道理的,到S.print str
被称为withFile
时已经获得并释放了句柄。
那么让我们在use
函数中移动流消耗:
use :: Handle -> IO ()
use h = do
S.print $ S.repeatM (hGetLine h)
并且提供hGetLine: invalid argument (invalid byte sequence)
。我不太清楚这个错误意味着什么。 isEOFError
是可以接受的,但'无效字节序列'?无论如何,这也不起作用。
我的想法已经用完......这是怎么做到的?
withFile
只是一个玩具示例,问题实际上是关于如何在bracket
内正确创建和使用流。
答案 0 :(得分:0)
让我们在使用功能中移动流消耗
这确实是正确的方法。
我在运行示例代码时实际上得到了正确的hGetLine: end of file
。问题是S.repeatM (hGetLine h)
从来不会检查它是否已到达文件的末尾,并在碰到它时抛出异常。
use
的以下定义没有出现这个问题:
use :: Handle -> IO ()
use h = do
S.print $ S.untilRight $ do eof <- System.IO.hIsEOF h
if eof then Right <$> pure ()
else Left <$> hGetLine h
它使用untilRight
函数。