本文介绍了将状态与IO操作组合在一起的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述 29岁程序员,3月因学历无情被辞! 假设我有一个状态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 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 likegetState :: (ST -> a) -> Op agetState 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) ... ) stsince 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.Arrayimport Control.Monad.Transdata 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 agetState g = Op $ \st -> return (st, g st)updState :: Monad m => (ST -> ST) -> Op m ()updState g = Op $ \st -> return (g st, ())testOpIO :: Op IO StringtestOpIO = do x <- lift getLine return xtest = runOp testOpIOThe key things to observe:The use of the MonadTrans classThe use of the lift function acting on getLine, which is used to bring the getline function from the IO monad and into the Op 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操作组合在一起的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持! 上岸,阿里云!
09-02 03:30