gpt4 book ai didi

haskell - 状态单子(monad) : Transitioning from one state type to another

转载 作者:行者123 更新时间:2023-12-03 22:18:39 24 4
gpt4 key购买 nike

假设我们有一堆单子(monad),其中状态单子(monad)变压器作为最外层的变压器,如下所示:

-- | SEWT: Composition of State . Except . Writer monad transformers in that
-- order where Writer is the innermost transformer.
-- the form of the computation is: s -> (Either e (a, s), w)
newtype SEWT s e w m a = SEWT {
_runSEWT :: StateT s (ExceptT e (WriterT w m)) a }
deriving (Functor, Applicative, Monad,
MonadState s, MonadError e, MonadWriter w)

-- | 'runSEWT': runs a 'SEWT' computation given an initial state.
runSEWT :: SEWT s e w m a -> s -> m (Either e (a, s), w)
runSEWT ev e = runWriterT $ runExceptT $ runStateT (_runSEWT ev) e

然后我们想以某种形式做: SEWT s e w m a -> s -> SEWT t e w m a .
这当然不可能使用 (>>=)do从带有 s 的状态单子(monad)开始阻塞因为 state 与 t 的单子(monad)不同.

然后我可以想出这样的东西:
-- | 'sewtTransition': transitions between one 'SEWT' computation with state s,
-- to another with state s. The current state and result of the given
-- computation is given to a mapping function that must produce the next
-- computation. The initial state must also be passed as the last parameter.
transitionState :: (Monad m, Monoid w) => ((a, s) -> SEWT t e w m a)
-> m (SEWT s e w m a) -> s -> m (SEWT t e w m a)
transitionState _trans _comp _init = do
(res, logs) <- _comp >>= flip runSEWT _init
return $ do tell logs
case res of Left fail -> throwError fail
Right succ -> _trans succ

-- 'withState': behaves like 'transitionState' but ignores the state of
-- the first computation.
withState :: (Monad m, Monoid w)
=> m (SEWT s e w m a) -> s -> m (SEWT t e w m a)
withState = transitionState $ return . fst

但是,从一种状态类型转移到另一种状态类型是否有更优雅、更通用的方法?

我对第二次计算不依赖于第一次计算的最终状态(仅结果)的解决方案以及它所在的解决方案都感兴趣。

Edit1:改进的过渡功能:
transSEWT :: Functor m => (((a, y), x) -> (a, y)) -> SEWT x e w m a -> x -> SEWT y e w m a
transSEWT f x_c x_i = SEWT $ StateT $ \y_i -> ExceptT . WriterT $
first ((\(a, x_f) -> f ((a, y_i), x_f)) <$>) <$> runSEWT x_c x_i

changeSEWT :: Functor m => SEWT x e w m a -> x -> SEWT y e w m a
changeSEWT = transSEWT fst

transS :: Monad m => (((a, y), x) -> (a, y)) -> StateT x m a -> x -> StateT y m a
transS f x_c x_i = StateT $ \y_i -> do (a, x_f) <- runStateT x_c x_i
return $ f ((a, y_i), x_f)

changeS :: Monad m => StateT x m a -> x -> StateT y m a
changeS = transS fst

最佳答案

你的想法可以用索引状态单子(monad)来实现。

newtype IState i o a = IState { runIState :: i -> (o, a) }
IState i o a 类型的值是一个有状态的计算,它返回一个 a 类型的值。 ,从 i 转换隐式状态的类型至 o进行中。将此与常规 State 进行对比monad,它不允许您更改其状态的类型:
type State s = IState s s

排序索引状态单子(monad)应确保输入和输出对齐。一个计算的输出类型是下一个计算的输入。输入 Atkey 的 parameterised monad (现在更普遍地称为索引单子(monad)),描述通过有向图的路径的类单子(monad)。
class IMonad m where
ireturn :: a -> m i i a
(>>>=) :: m i j a -> (a -> m j k b) -> m i k b

(>>>) :: IMonad m => m i j a -> m j k b -> m i k b
mx >>> my = mx >>>= const my

绑定(bind)一个带索引的 monad 就像玩多米诺骨牌:如果你有办法从 ij以及从 j 获取的方法至 k , >>>=将把你的多米诺骨牌粘在一起,形成一个更大的计算,该计算来自 ik . McBride 在 Kleisli Arrows of Outrageous Fortune 中描述了这个索引单子(monad)的更强大的版本。 ,但这对于我们的目的来说已经足够了。

正如我上面所描述的,多米诺骨牌式的排序正是索引状态单子(monad)所需要的,它需要输入和输出的对齐。
instance IMonad IState where
ireturn x = IState $ \s -> (s, x)
IState f >>>= g = IState $ \i -> let (o, x) = f i
in runIState (g x) o

从索引状态单子(monad)中检索值不会更改状态的类型。
get :: IState s s s
get = IState $ \s -> (s, s)

将值放入索引状态 monad 会丢弃旧状态。这意味着输入状态的类型可以是任何你喜欢的。
put :: s -> IState i s ()
put x = IState $ \_ -> (x, ())

请注意,所有与 IState 一起使用的代码与 State 完全相同!它只是变得更聪明的类型。

这是一个简单的 IState期望 Int 类型状态的计算, 将状态更改为 String , 并返回一个 bool 答案。所有这些都是静态检查的。
myStateComputation :: IState Int String Bool
myStateComputation =
-- poor man's do notation. You could use RebindableSyntax
get >>>= \s ->
put (show s) >>>
ireturn (s > 5)

main = print $ runIState myStateComputation 3
-- ("3", False)

关于haskell - 状态单子(monad) : Transitioning from one state type to another,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37062157/

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