Haskell 读取任意数量的二进制消息

Haskell 读取任意数量的二进制消息,haskell,binary,monads,Haskell,Binary,Monads,我正在使用binary.Get解析文件中的二进制数据,并具有如下内容: data FileMessageHeaders = FileMessageHeaders [FileMessageHeader] data FileMessageHeader = FileMessageHeader ... instance Binary FileMessageHeaders where put = undefined get = do messages <- untilM get

我正在使用binary.Get解析文件中的二进制数据,并具有如下内容:

data FileMessageHeaders = FileMessageHeaders [FileMessageHeader]

data FileMessageHeader = FileMessageHeader ...

instance Binary FileMessageHeaders where
  put = undefined
  get = do
    messages <- untilM get isEmpty
    return (FileMessageHeaders messages)

instance Binary FileMessageHeader where
  put = undefined
  get = ..
data FileMessageHeaders=FileMessageHeaders[FileMessageHeader]
数据FileMessageHeader=FileMessageHeader。。。
实例二进制FileMessageHeaders,其中
put=未定义
得到=做

消息这里的问题是,
IO
操作必须完成才能继续控制流。因此,程序必须先读入所有消息,然后才能对它们进行评估。您可以尝试定义自己的组合器
sequenceI
,该组合器使用
System.IO.Unsafe
中的函数。这个函数允许您交错操作。例如,
getContents
使用它。我会这样定义
sequenceI

sequenceI (x:xs) = do v <- x
                      vs <- unsafeInterleaveIO $ sequenceI xs
                      return (v:vs)

这里的问题是,
IO
操作必须在控制流继续之前完成。因此,程序必须先读入所有消息,然后才能对它们进行评估。您可以尝试定义自己的组合器
sequenceI
,该组合器使用
System.IO.Unsafe
中的函数。这个函数允许您交错操作。例如,
getContents
使用它。我会这样定义
sequenceI

sequenceI (x:xs) = do v <- x
                      vs <- unsafeInterleaveIO $ sequenceI xs
                      return (v:vs)

正如fuzzxl所指出的,问题是
直到lm
Get
monad是严格的,要求整个
untilM
操作在返回之前完成。伊奥与此无关

最简单的方法可能是切换到并使用它进行解析,而不是二进制。Attoparsec支持流式解析,在这种情况下可能更容易使用

如果无法切换到attoparsec,则需要使用一些较低级别的二进制函数,而不仅仅是使用
binary
实例。类似于以下内容(完全未经测试)


不幸的是,这意味着您将无法使用
Binary
实例或
get
函数,您必须使用
getHeaders
。不过它会流式传输。

正如Fuzzxl所指出的,问题是
一直到
Get
monad是严格的,要求整个
untilM
操作在返回之前完成。伊奥与此无关

最简单的方法可能是切换到并使用它进行解析,而不是二进制。Attoparsec支持流式解析,在这种情况下可能更容易使用

如果无法切换到attoparsec,则需要使用一些较低级别的二进制函数,而不仅仅是使用
binary
实例。类似于以下内容(完全未经测试)


不幸的是,这意味着您将无法使用
Binary
实例或
get
函数,您必须使用
getHeaders
。不过它会流式播放。

风格提示:当你刚刚包装东西时,使用
newtype
或简单的
type
。编译器可以删除
newtype
s。半生不熟的答案:使用惰性IO、使用迭代对象,或者在准备处理下一条记录时以增量方式返回结果并读取下一条记录(一个不太安全但更易理解的迭代对象版本)。风格说明:使用
newtype
或简单的
类型
,当你正在包装东西的时候。编译器可以删除
newtype
s。半生不熟的答案:使用惰性IO,使用迭代对象,或者在准备处理下一条记录时以增量方式返回结果并读取下一条记录(一个不太安全但更容易理解的迭代对象版本)。我正试图使用它作为基础来重新定义untilM,但是我在排列类型时遇到了麻烦,我不断地得到“无法匹配预期的IO a0类型和实际的Get t0类型”知道我如何处理这个问题吗?谢谢你的回复。我让它工作起来了,但我仍然在打字方面有问题。我用untilMI替换untilM,得到“无法将预期类型
Get t0'与实际类型匹配”
IO[a0]”我尝试在untilMI前面使用liftIO,得到“无法将预期类型
IO a0'与实际类型匹配”
Get t0“有人知道我如何让unsafeInterleaveIO与GetMonad一起工作吗?拉兹:我看错了类型签名。现在应该可以工作了。但是我看到了另一个问题:由于
get
可以在任何monad中工作,
unsafePerformIO
太专业化了……是的,我实际上没有在上一次编辑中删除f,所以当我遇到这些错误时,上面的代码就是我试图运行的。@fuzzxl:您正确地查看
直到
,但是你的答案混淆了IO和其他单子,并且对这个问题具有误导性。特别是,
unsafePerformIO
根本无法应用。因此,我试图重新定义直到使用它作为基础,但是我在类型排列上遇到了问题,我不断得到“无法匹配预期的IO a0类型和实际的Get t0类型”知道我如何处理这个问题吗?谢谢你的回复。我让它工作起来了,但我仍然在打字方面有问题。我用untilMI替换untilM,得到“无法将预期类型
Get t0'与实际类型匹配”
IO[a0]”我尝试在untilMI前面使用liftIO,得到“无法将预期类型
IO a0'与实际类型匹配”
Get t0“有人知道我如何让unsafeInterleaveIO与GetMonad一起工作吗?拉兹:我看错了类型签名。现在应该可以工作了。但是我看到了另一个问题:由于
get
可以在任何monad中工作,
unsafePerformIO
太专业化了……是的,我实际上没有在上一次编辑中删除f,所以当我遇到这些错误时,上面的代码就是我试图运行的。@fuzzxl:您正确地查看
直到
,但是你的答案混淆了IO和其他单子,并且对这个问题具有误导性。特别是,
unsafePerformIO
根本无法应用。因此
getHeaders :: ByteString -> [FileMessageHeader]
getHeaders b = go b 0
  where
    go bs n
      | B.null bs = []
      | otherwise = let (header, bs', n') = runGetState get bs n
                    in header : go bs' n'