更严格的严格状态Monad

yon*_*ong 13 haskell lazy-evaluation state-monad

严格的状态monad定义使用:

m >>= k = State $ \s ->
  case runState m s of
    (a, s') -> runState (k a) s'
Run Code Online (Sandbox Code Playgroud)

但这仍然会泄漏记忆,因为a并且没有s'得到评估.例如,我们可能有一个函数f将大对象作为输入并快速返回(a, s'),但只要a没有评估,输入f就不能被GC.

一个可能的解决方案是f返回seq a (a, s'),但如果我们使用类似的东西MonadRandom,这并不总是可能的,并且状态被封装起来f.是否有这样定义的版本:

m >>= k = State $ \s ->
  case runState m s of
    (!a, !s') -> runState (k a) s'
Run Code Online (Sandbox Code Playgroud)

这已存在于任何地方的图书馆吗?

dfe*_*uer 6

根据monad身份法,

return a >>= const b = const b a = b
Run Code Online (Sandbox Code Playgroud)

特别是,

return undefined >>= const b = b
Run Code Online (Sandbox Code Playgroud)

如果>>=操作对结果值严格,那将破坏这个定律,所以你不应该这样做.

假设您这样做:

m >>= k = State $ \s ->
  case runState m s of
    (a, !s') -> runState (k a) s'
Run Code Online (Sandbox Code Playgroud)

现在我们面临另一个身份法:

m >>= return = m
Run Code Online (Sandbox Code Playgroud)

例如,

return a >>= return = return a
Run Code Online (Sandbox Code Playgroud)

所以如果return a >>= return是严格的状态,那么我们也必须return a在国家严格!所以我们也需要重新定义return:

return a = State $ \ !s -> (a, s)
Run Code Online (Sandbox Code Playgroud)

请注意,您实际上不需要执行任何操作; 如果你愿意,你可以使用通常的严格状态monad,并写出类似的东西

!_ <- get
Run Code Online (Sandbox Code Playgroud)

在你想强迫国家的地方.你甚至可以写一个动作来做到这一点:

forceState :: Monad m => StateT s m ()
forceState = get >>= \ !_ -> return ()
Run Code Online (Sandbox Code Playgroud)

编辑

即使这个定义对我来说也有点奇怪; 我希望lambda强迫国家,而不是case.我不确定如果不这样做会导致某种破损,但如果确实如此,我不会感到惊讶.

  • 在存在 `seq` 的情况下,所有 monad 都不符合规律(还有惰性的 `Writer` 和 `State`),请参阅 [this answer](http://stackoverflow.com/a/12620418/1333025)。 (3认同)
  • @PetrPudlák,特别是,我认为最好确保你的 `Monad` 实例遵守法律*,只要没有人使用它们做愚蠢的 `seq` 技巧*。 (2认同)
  • 在基础版本 4.8 中,愚蠢的 `seq` 技巧将更加讨厌,因为 `Identity` 将充分利用不这样做的人,定义为 `fmap=coerce`、`foldMap=coerce`、`foldl=coerce`,和 `foldl'=coerce`。 (2认同)
  • 参见[coinduction](https://en.wikipedia.org/wiki/Coinduction),[corecursion](https://en.wikipedia.org/wiki/Corecursion)以及[Total functional programming](https:// en.wikipedia.org/wiki/Total_functional_programming).阅读链接[论文](http://www.jucs.org/jucs_10_7/total_functional_programming)也是值得的.特别是,归纳数据类型总是有限的并且没有底部,共同数据类型(codata)可以是无限的,但同样没有底部.什么是非常好的,这可以在语法上强制执行.所以`seq`只会改变性能,而不是结果. (2认同)