gpt4 book ai didi

haskell - 与 State monad 喜结良缘

转载 作者:行者123 更新时间:2023-12-03 07:21:09 24 4
gpt4 key购买 nike

我正在开发一个 Haskell 项目,该项目涉及到一个大结:我正在解析图的序列化表示,其中每个节点都位于文件中的某个偏移量,并且可以通过其偏移量引用另一个节点。因此,我需要在解析时构建从偏移量到节点的映射,我可以在 do rec 中将其反馈给自己。 block 。

我有这个工作,并且有点合理地抽象为 StateT -esque monad 变压器:

{-# LANGUAGE DoRec, GeneralizedNewtypeDeriving #-}

import qualified Control.Monad.State as S

data Knot s = Knot { past :: s, future :: s }

newtype RecStateT s m a = RecStateT (S.StateT (Knot s) m a) deriving
( Alternative
, Applicative
, Functor
, Monad
, MonadCont
, MonadError e
, MonadFix
, MonadIO
, MonadPlus
, MonadReader r
, MonadTrans
, MonadWriter w )

runRecStateT :: RecStateT s m a -> Knot s -> m (a, Knot s)
runRecStateT (RecStateT st) = S.runStateT st

tie :: MonadFix m => RecStateT s m a -> s -> m (a, s)
tie m s = do
rec (a, Knot s' _) <- runRecStateT m (Knot s s')
return (a, s')

get :: Monad m => RecStateT s m (Knot s)
get = RecStateT S.get

put :: Monad m => s -> RecStateT s m ()
put s = RecStateT $ S.modify $ \ ~(Knot _ s') -> Knot s s'

tie function 是神奇发生的地方:调用runRecStateT产生一种值(value)和一种状态,我将其作为它自己的 future 。请注意get允许您读取过去和 future 的状态,但是 put只允许您修改“当前”。

问题 1:总体来说,这似乎是实现这种打结模式的好方法吗?或者更好的是,有人对此实现了一个通用的解决方案,而我在通过 Hackage 进行窥探时忽略了这个问题吗?我用头撞了Cont monad 一段时间,因为它看起来可能更优雅(参见 Dan Burton 的 similar post),但我就是无法解决。

完全主观的问题 2:我对调用代码最终的样子并不完全满意:

do
Knot past future <- get
let {- ... -} = past
{- ... -} = future
node = {- ... -}
put $ {- ... -}
return node

这里省略了实现细节,显然,重要的是我必须得到 pastfuture状态,在 let 绑定(bind)内对它们进行模式匹配(或显式地使先前的模式变得惰性)以提取我关心的任何内容,然后构建我的节点,更新我的状态,最后返回节点。似乎不必要地冗长,而且我特别不喜欢意外地创建提取 past 的模式是多么容易。和future规定严格。那么,有人能想到更好的界面吗?

最佳答案

我一直在尝试一些东西,我想我已经想出了一些......有趣的东西。我将其称为“Seer”monad,它提供(除了 Monad 操作之外)两个原始操作:

see  :: Monoid s => Seer s s
send :: Monoid s => s -> Seer s ()

和运行操作:

runSeer :: Monoid s => Seer s a -> a

这个 monad 的工作方式是,see 允许预言者看到一切,而 send 允许预言者将信息“发送”到所有其他的看见者都让他们看到。每当任何看见者执行 see 操作时,他们就能够看到所有已发送的信息以及所有将要发送的信息。换句话说,在给定的运行中,无论何时何地调用 see 都将始终产生相同的结果。另一种说法是,see 是获取“打结”结的工作引用的方式。

这实际上与仅使用 fix 非常相似,只不过所有子部分都是增量且隐式添加的,而不是显式添加的。显然,在存在悖论的情况下,预言家将无法正确工作,因此需要足够的懒惰。例如,see >>= send 可能会导致信息爆炸,使您陷入时间循环。

一个愚蠢的例子:

import Control.Seer
import qualified Data.Map as M
import Data.Map (Map, (!))

bar :: Seer (Map Int Char) String
bar = do
m <- see
send (M.singleton 1 $ succ (m ! 2))
send (M.singleton 2 'c')
return [m ! 1, m ! 2]

正如我所说,我只是在闲逛,所以我不知道这是否比你得到的更好,或者它是否有任何好处!但它很漂亮,而且很相关,如果你的“结”状态是一个Monoid,那么它可能对你有用。公平警告:我使用 Tardis 构建了 Seer

https://github.com/DanBurton/tardis/blob/master/Control/Seer.hs

关于haskell - 与 State monad 喜结良缘,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11060565/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com