我想知道什么可以被认为是关于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
时我可以看到的优点如下:
- 解析器函数的返回类型是统一的
- 无需调用
lift
来访问解析器原语。
我能看到的缺点是:
- 高级解析器和低级原语之间没有区别。
- 我们不能清楚地告诉什么时候头被完全解析或什么时候没有。这使得解析器的修改更加脆弱。
谢谢。
一般来说,我建议不要使用多层State
(或任何变压器)。transformer很棒,但是在较厚的集群中,它们确实会令人困惑,特别是当类型系统无法正确决定使用哪个MonadState
时。
然而,在您的特定情况下,另一个转换器实际上是一个好主意,但不是一个StateT
:头信息不应该在文件的进一步解析期间改变,所以它应该真的只是一个ReaderT
,不是吗?
type ParserFullState = ReaderT Header (State ParserState)
或者
type ParserFullState = RSS Header () ParserState