使用liftIO
你已经很接近了!您的建议
newtype Op a = Op {runOp :: ST -> IO (ST, a)}
非常好,而且是要走的路。
为了能够执行getLine
in an Op
上下文,你需要“提升”IO
操作进入Op
单子。您可以通过编写一个函数来做到这一点liftIO
:
liftIO :: IO a -> Op a
liftIO io = Op $ \st -> do
x <- io
return (st, x)
你现在可以写:
runOp (do x <- liftIO getLine; ...
使用 MonadIO 类
现在,将 IO 操作提升到自定义 monad 的模式非常常见,以至于有一个标准类型类:
import Control.Monad.Trans
class Monad m => MonadIO m where
liftIO :: IO a -> m a
这样你的版本liftIO
成为一个实例MonadIO
反而:
instance MonadIO Op where
liftIO = ...
使用状态T
您当前已经编写了自己的状态单子版本,专门用于状态ST
。为什么不使用标准状态 monad?它使您不必自己编写Monad
实例,对于状态单子来说总是相同的。
type Op = StateT ST IO
StateT
已经有一个Monad
实例和一个MonadIO
实例,这样您就可以立即使用它们。
单子变压器
StateT
是一个所谓的单子变压器。你只想IO
你的行动Op
monad,所以我已经将它专门化为IO
monad 适合你(参见定义type Op
)。但是 monad 转换器允许您堆叠任意 monad。这就是inoverflow所说的。您可以阅读有关他们的更多信息here http://blog.sigfpe.com/2006/05/grok-haskell-monad-transformers.html and here http://en.wikibooks.org/wiki/Haskell/Monad_transformers.