Created: 2023-02-24 17:59

Given MaybeT and StateT:

newtype StateT s m a = StateT { runStateT :: s -> m (a, s) }
 
instance Functor (StateT s m) where
instance Applicative (StateT s m) where
instance Monad m => Monad (StateT s m) where
 
newtype MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) }
 
instance Functor (MaybeT m) where
instance Applicative (MaybeT m) where
 
instance Monad m => Monad (MaybeT m) where
  x >>= f = MaybeT $ do 
    ma <- runMaybeT x
    case ma of
      Nothing -> pure Nothing
      Just a -> runMaybeT $ f a

What would be the different in having MaybeT .. StateT vs. StateT .. MaybeT?

The former produces the state as it was before the error occurred. In other words, the state is maintained and returned, alongside the potential result.

runMaybeState :: MaybeT (StateT s Identity) a -> s -> (Maybe a, s)
runMaybeState act s = runIdentity $ runStateT (runMaybeT act) s

The later introduces transaction semantics, it produces both the state and result if there’s a result.

runStateMaybe :: StateT s (MaybeT Identity) a -> s -> Maybe (a, s)
runStateMaybe act s = runIdentity $ runMaybeT (runStateT act s)