将状态与IO操作组合在一起 [英] Combine state with IO actions
问题描述
假设我有一个状态monad,比如:
data寄存器= Reg {...}
data ST = ST {registers :: Registers,
memory :: Array Int Int}
newtype Op a = Op {runOp :: ST - > (ST,a)}
实例Monad Op其中
返回a = Op $ \st - > (st,a)
(>> =)stf f = Op $ \st - > let(st1,a1)= runOp stf st
(st2,a2)= runOp(f a1)st1
in(st2,a2)
具有如下功能:
getState ::(ST - > a) - > Op a
getState g = Op(\ st - >(st,g st)
updState ::(ST - > ST) - > Op()
updState g = Op(\ st - >(g st,()))
和我想将这个monad中的各种操作与IO操作结合起来,因此我可以编写一个评估循环,在这个循环中执行这个monad中的操作,并执行一个IO操作和结果,或者我认为,我应该能够做到如下所示:
newtype Op a = Op {runOp :: ST - > IO(ST,a )}
打印函数的类型为Op(),其他函数的类型为Op a,我可以使用IO Char类型的函数从终端读取一个字符,但是我不确定这样的函数是什么样子的,因为例如下面的代码无效。
runOp(do x< - getLine; setMem 10 ...(read x :: Int)...)st
因为getLine的类型为I O Char,但是这个表达式应该有Op Char类型。总的来说,我该如何做到这一点?
基本方法是重写你的 Op
monad作为monad转换器。这将允许你使用它在monads的堆栈中,它的底部可以是 IO
。
下面是一个可能的例子:
import Data.Array
import Control.Monad.Trans
data Registers = Reg {foo :: Int}
data ST = ST {registers :: Registers,
memory :: Array Int Int}
newtype Op ma = Op {runOp :: ST - > m(ST,a)}
实例Monad m => Monad(Op m)其中
返回a = Op $ \st - > return(st,a)
(>> =)stf f = Op $ \st - > (st1,a1)< - runOp stf st
(st2,a2)< - runOp(f a1)st1
返回(st2,a2)
实例MonadTrans Op其中
lift m = Op $ \st - >做一个< - m
return(st,a)
getState :: Monad m => (ST→a)→> Op m a
getState g = Op $ \st - > return(st,g st)
updState :: Monad m => (ST→ST)→> Op m()
updState g = Op $ \st - > return(g st,())
testOpIO :: Op IO String
testOpIO = do x < - lift getLine
return x
test = runOp testOpIO
关键要注意的是:
使用 MonadTrans
class
在 getLine
上运行
函数,该函数用于将 getline
函数从 IO
monad,并进入 Op IO
monad。顺便提一句,如果您不希望 IO
monad始终存在,您可以用 Identity
monad在 Control.Monad.Identity
中。 操作标识
monad的行为与原始操作
monad完全相同。
Suppose I have a state monad such as:
data Registers = Reg {...}
data ST = ST {registers :: Registers,
memory :: Array Int Int}
newtype Op a = Op {runOp :: ST -> (ST, a)}
instance Monad Op where
return a = Op $ \st -> (st, a)
(>>=) stf f = Op $ \st -> let (st1, a1) = runOp stf st
(st2, a2) = runOp (f a1) st1
in (st2, a2)
with functions like
getState :: (ST -> a) -> Op a
getState g = Op (\st -> (st, g st)
updState :: (ST -> ST) -> Op ()
updState g = Op (\st -> (g st, ()))
and so forth. I want to combine various operations in this monad with IO actions. So I could either write an evaluation loop in which operations in this monad were performed and an IO action is executed with the result, or, I think, I should be able to do something like the following:
newtype Op a = Op {runOp :: ST -> IO (ST, a)}
Printing functions would have type Op () and other functions would have type Op a, e.g., I could read a character from the terminal using a function of type IO Char. However, I'm not sure what such a function would look like, since e.g., the following is not valid.
runOp (do x <- getLine; setMem 10 ... (read x :: Int) ... ) st
since getLine has type IO Char, but this expression would have type Op Char. In outline, how would I do this?
The basic approach would be to rewrite your Op
monad as a monad transformer. This would allow you to use it in a "stack" of monads, the bottom of which might be IO
.
Here's an example of what that might look like:
import Data.Array
import Control.Monad.Trans
data Registers = Reg { foo :: Int }
data ST = ST {registers :: Registers,
memory :: Array Int Int}
newtype Op m a = Op {runOp :: ST -> m (ST, a)}
instance Monad m => Monad (Op m) where
return a = Op $ \st -> return (st, a)
(>>=) stf f = Op $ \st -> do (st1, a1) <- runOp stf st
(st2, a2) <- runOp (f a1) st1
return (st2, a2)
instance MonadTrans Op where
lift m = Op $ \st -> do a <- m
return (st, a)
getState :: Monad m => (ST -> a) -> Op m a
getState g = Op $ \st -> return (st, g st)
updState :: Monad m => (ST -> ST) -> Op m ()
updState g = Op $ \st -> return (g st, ())
testOpIO :: Op IO String
testOpIO = do x <- lift getLine
return x
test = runOp testOpIO
The key things to observe:
- The use of the
MonadTrans
class - The use of the
lift
function acting ongetLine
, which is used to bring thegetline
function from theIO
monad and into theOp IO
monad.
Incidentally, if you don't want the IO
monad to always be present, you can replace it with the Identity
monad in Control.Monad.Identity
. The Op Identity
monad behaves exactly the same as your original Op
monad.
这篇关于将状态与IO操作组合在一起的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!