简体   繁体   English

州monad:从一种州转变为另一种州

[英]State monads: Transitioning from one state type to another

Let's say we have a stack of monads with a state monad transformer as the outer most transformer like this one: 假设我们有一堆具有状态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

We then want to do, in some form: SEWT sewma -> s -> SEWT tewma . 然后我们想以某种形式做: SEWT sewma -> s -> SEWT tewma This is of course not possible using (>>=) or a do block since a state monad with s as state is not the same monad as one with t . 这当然不可能使用(>>=)do块,因为具有s作为状态的状态monad与具有t的状态monad不同。

I can then conjure up something like this: 然后我可以想出这样的事情:

-- | '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

But is there perhaps a more elegant and general way to move from one state type to another? 但是,是否有更优雅和一般的方式从一种状态转变为另一种状态?

I'm interested both in solutions where the second computation is not dependent on the final state (only the result) of the first computation, and one where it is. 我对两个解决方案感兴趣,其中第二个计算不依赖于第一个计算的最终状态(仅结果),以及它的一个位置。

Edit1: Improved transition functions: 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

Your idea can be implemented with the indexed state monad . 您的想法可以使用索引状态monad实现

newtype IState i o a = IState { runIState :: i -> (o, a) }

A value of type IState ioa is a stateful computation which returns a value of type a , transforming the type of the implicit state from i to o in the process. IState ioa类型的值是有状态计算,它返回类型a的值,在进程中将隐式状态的类型从io Contrast this with the regular State monad, which doesn't allow you to change the type of its state: 将此与常规State monad进行对比,后者不允许您更改其状态类型:

type State s = IState s s

Sequencing indexed state monads should ensure that the inputs and outputs line up. 排序索引状态monad应确保输入和输出对齐。 The output type of one computation is the input of the next. 一次计算的输出类型是下一次计算的输入。 Enter Atkey's parameterised monad (now more commonly known as the indexed monad ), the class of monad-like things describing a path through a directed graph. 输入Atkey的参数化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

Binding an indexed monad is like playing dominoes: if you have a way to get from i to j and a way to get from j to k , >>>= will glue your dominoes together into a bigger computation which goes from i to k . 绑定一个索引的monad就像玩多米诺骨牌:如果你有办法从ij以及从jk>>>=会将你的多米诺骨牌粘合成一个更大的计算,从ik McBride describes a more powerful version of this indexed monad in Kleisli Arrows of Outrageous Fortune , but this one is quite enough for our purposes. 麦克布赖德描述了这个索引monad在Kleisli Arrows of Outrageous Fortune中更强大的版本,但是这个版本足以满足我们的目的。

As I described above, domino-like sequencing is just what's needed for the indexed state monad, which requires alignment of inputs and outputs. 如上所述,多米诺骨牌式排序正是索引状态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

Retrieving a value from an indexed state monad doesn't change the type of the state. 从索引状态monad中检索值不会更改状态的类型。

get :: IState s s s
get = IState $ \s -> (s, s)

Putting a value into the indexed state monad discards the old state. 将值放入索引状态monad会丢弃旧状态。 This means the type of the input state can be anything you like. 这意味着输入状态的类型可以是您喜欢的任何类型。

put :: s -> IState i s ()
put x = IState $ \_ -> (x, ())

Note that all the code to work with IState is exactly the same as State ! 请注意,所有一起工作的代码IState是完全一样的State It's just the types which have got smarter. 它只是变得更聪明的类型。

Here's a simple IState computation which expects a state of type Int , changes the state to a String , and returns a Boolean answer. 这是一个简单的IState计算,它需要Int类型的状态,将状态更改为String ,并返回一个布尔应答。 All of this is statically checked. 所有这些都是静态检查。

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)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM