Monad的最佳实践



我想知道什么可以被认为是关于State单子的最佳实践。我也愿意接受任何其他建议。

我有一个二进制文件要解析。它包含不同的头文件,为了能够读取完整的文件,需要对这些头文件进行解析。

所以头可以只使用解析中的State来解析。

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

然后在State单子中使用该数据

type Parser a = State ParseState a

这可以完美地配合头文件的解析。但是,一旦我想解析完整的文件,我就需要头文件中的信息,以便能够正确读取文件。

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

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

我的想法是使用一个新的状态单子,它位于前一个单子的顶部。所以我有一个新的StateT单子:

type ParserFullState a = StateT Header (State ParserState) a
因此,我可以继续使用新的状态转换器构建一整套解析器函数。我也可以用不同的方式来做,并将标题添加到原始的ParseState数据。

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

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

我能看到的缺点是:

  1. 高级解析器和低级原语之间没有区别。
  2. 我们不能清楚地告诉什么时候头被完全解析或什么时候没有。这使得解析器的修改更加脆弱。
你的建议是什么?我是否应该使用状态转换器或者我是否应该将标题添加到原始状态或其他任何东西?

谢谢。

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

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

type ParserFullState = ReaderT Header (State ParserState)

或者

type ParserFullState = RSS Header () ParserState

最新更新