Monad的最佳实践

时间:2014-08-14 13:36:08

标签: haskell monads state-monad

我想知道什么可以被视为关于State monad的最佳做法。我也对任何其他建议持开放态度。

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

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

data ParseState = ParseState {
   offset :: Int64
   buffer :: B.ByteString
   endianness :: Endianness
   pointerSize :: MachineWord
   positionStack :: [Int64]
}

然后在State monad

中使用此数据
type Parser a = State ParseState a

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

data Header = Header {
    txtOffset :: Int64,
    stringOffset :: Int64
}

我需要标头信息才能继续解析文件。

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

type ParserFullState a = StateT Header (State ParserState) a

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

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

  1. 解析器函数的返回类型是统一的
  2. 无需调用lift来访问解析器原语。
  3. 我能看到的缺点是:

    1. 高级解析器和低级原语之间没有区别。
    2. 我们无法清楚地说明标头何时完全解析或何时不完整。从而使解析器修改更加脆弱。
    3. 你的建议是什么?我应该使用状态转换器吗?我应该将标头添加到原始状态还是其他任何地方?

      感谢。

1 个答案:

答案 0 :(得分:7)

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

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

type ParserFullState = ReaderT Header (State ParserState)

equivalently

type ParserFullState = RSS Header () ParserState