你可以看看StateT是如何实现的:
newtype StateT s m a = StateT { runStateT :: s -> m (a,s) }
要将状态与IO 结合,只需将IO 替换为m 并获得所需的类型:s -> IO (a,s)。
如果您也有错误,这将变成类似s -> IO (Either e (a, s)) 或s -> IO (Either e a, s) 的内容,具体取决于您是否希望失败的计算影响状态。
请注意,如果没有时间机器,您无法将 s -> Either e (IO (a, s)) 设为 monad。
更新
事实证明,即使使用时间机器,您也无法将其变成 monad。
为了说明为什么这是不可能的,让我们首先使用() 而不是s 来简化我们的monad:data M e a = M { runM :: Either e (IO a) }
现在,想象一下下面的程序:
unsafePerformIO :: IO a -> a
unsafePerformIO io = fromLeft $ runM $ do
a <- M $ Right $ io
M $ Left a
显然,这个函数是不可能的,因此 M 的 monad 实例也是不可能的。
时间机器能给你的是对待IO的能力,就像对待State一样。但是,我没有意识到 Either e (s -> (a, s)) 不是单子。