Предположим, что у меня есть монада состояний, такая как:
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)
с функциями типа
getState :: (ST -> a) -> Op a
getState g = Op (\st -> (st, g st)
updState :: (ST -> ST) -> Op ()
updState g = Op (\st -> (g st, ()))
и т.д. Я хочу объединить различные операции в этой монаде с действиями IO. Поэтому я мог бы написать цикл оценки, в котором выполнялись операции в этой монаде, и с результатом выполнялось действие 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 имеет тип IO Char, но это выражение будет иметь тип Op Char. В общих чертах, как бы я это сделал?