【问题标题】:Best practice with MonadMonad 的最佳实践
【发布时间】:2014-10-08 04:26:24
【问题描述】:

我想知道关于State monad 的最佳实践是什么。我也愿意接受任何其他建议。

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

因此可以仅使用解析中的状态来解析标头。

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
}

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

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

type ParserFullState a = StateT Header (State ParserState) a

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

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

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

我能看到的缺点是:

  1. 高级解析器和低级原语没有区别。
  2. 我们无法清楚地判断标头何时已完全解析,何时未完全解析。从而使解析器修改更加脆弱。

你有什么建议?我应该使用状态转换器,我应该将标头添加到原始状态还是其他任何东西?

谢谢。

【问题讨论】:

  • 为什么不 data ParseState' = Initial ParseState | Later ParseState Header 或其他适合您的问题的 ADT,这样您只有一个状态可以代表您的初始解析状态,然后当您需要 Header 时,您同时拥有 @ 987654332@ 和Header 信息是分开的吗?获得标题信息后,您只需 put (Later parseState header)

标签: haskell monads state-monad


【解决方案1】:

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

尽管如此,在您的特定情况下,另一个转换器实际上是个好主意,但不是StateT:在进一步解析文件期间,标题信息不应更改,因此它应该只是ReaderT,不应该吗?

type ParserFullState = ReaderT Header (State ParserState)

equivalently

type ParserFullState = RSS Header () ParserState

【讨论】:

  • 请注意,只有使用 mtl 库时,转换器才会令人困惑。如果您使用带有显式lifts 的transformers 库,则很简单。
  • @GabrielGonzalez:不会让类型系统感到困惑,但它仍然会让程序员感到困惑!
猜你喜欢
  • 1970-01-01
  • 2021-12-08
  • 2014-04-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-05
相关资源
最近更新 更多