Monad的最佳实践

mat*_*thk 1 monads haskell state-monad

我想知道什么可以被视为关于Statemonad 的最佳实践.我也对任何其他建议持开放态度.

我有一个要解析的二进制文件.它包含需要解析的不同标头,以便能够读取整个文件.

因此,只能使用解析中的State来解析头文件.

data ParseState = ParseState {
   offset :: Int64
   buffer :: B.ByteString
   endianness :: Endianness
   pointerSize :: MachineWord
   positionStack :: [Int64]
}
Run Code Online (Sandbox Code Playgroud)

然后将该数据用于Statemonad中

type Parser a = State ParseState a
Run Code Online (Sandbox Code Playgroud)

这可以完美地解决标头的解析.但是,只要我想解析整个文件,我需要标题中的信息才能正确读取文件.

data Header = Header {
    txtOffset :: Int64,
    stringOffset :: Int64
}
Run Code Online (Sandbox Code Playgroud)

我需要标头信息来继续解析文件.

我的想法是使用一个位于前一个状态之上的新状态monad.所以我有一个新的StateT monad:

type ParserFullState a = StateT Header (State ParserState) a
Run Code Online (Sandbox Code Playgroud)

因此,我可以继续使用新的状态转换器构建一整套解析器函数.我也可以采用不同的方式将标题添加到原始ParseState数据中.

我可以看到将标题添加回ParserState以下内容的优点如下:

  1. 解析器函数的返回类型是统一的
  2. 无需调用lift来访问解析器原语.

我能看到的缺点是:

  1. 高级解析器和低级原语之间没有区别.
  2. 我们无法清楚地说明标头何时完全解析或何时不完整.从而使解析器修改更加脆弱.

你的建议是什么?我应该使用状态转换器吗?我应该将标头添加到原始状态还是其他任何情况?

谢谢.

lef*_*out 7

一般来说,我建议不要使用多层State(或实际上任何变压器).变形金刚很棒,但在较厚的集群中,它们确实会让人感到困惑,特别是当类型系统无法正确决定MonadState使用哪个时.

然而,在你的具体情况下,另一个变换器实际上是一个好主意,但不是a StateT:在进一步解析文件时头文件信息不应该改变,所以它应该真的只是一个ReaderT,不应该吗?

type ParserFullState = ReaderT Header (State ParserState)
Run Code Online (Sandbox Code Playgroud)

或者等价的

type ParserFullState = RSS Header () ParserState
Run Code Online (Sandbox Code Playgroud)

  • @GabrielGonzalez:不要混淆类型系统,但它仍然会让*程序员*感到困惑! (2认同)