How to call partially un-nest transformers? - haskell

Given a nesting of transformers :: T2 of T1 of M0, how can I utilize :: T2 of M0, :: M2 ?
Here's an example: I am writing some function which requires reading, logging, and state. The definition:
gameRoutine :: WriterT [String] (ReaderT Env (State Viable)) NUM
If I want to call some
stateFunction :: State Viable NUM
Or even stateWithReaderFunction :: ReaderT Env (State Viable) NUM,
I can use lift:
gameRoutine = do
x <- lift . lift $ stateFunction
y <- lift $ stateWithReaderFunction
But how do I call writerFunction :: Writer [String] a ?
How do I call writerStateFunction :: WriterT [String] (State Viable) NUM (the difference between gameRoutine definition is ReaderT layer missing)?
Clearly I don't want to lift their definitions to one of gameRoutine.

Well, basically the idea of mtl is that you don't need to do that. Instead of defining writerStateFunction with that specific signature, you define it with a generic signature
writerStateFunction' :: (MonadWriter [String] m, MonadState Viable m)
=> m NUM
Then it doesn't matter that the environment where you try to use this has an extra Reader layer: this doesn't prevent the monad from having both MonadWriter and MonadState functionality.

You can lift a Writer w a into a MonadWriter instance by, something like:
import Control.Monad.Writer (MonadWriter, Writer, runWriter, tell)
lift' :: MonadWriter w m => Writer w a -> m a
lift' wr = do
let (a, w) = runWriter wr
tell w -- manually re-log the log msg
return a -- wrap value into new context
then, you may simply write:
gameRoutine :: WriterT [String] (ReaderT Env (State Viable)) NUM
gameRoutine = do
a <- lift' writerFunction
as long as writerFunction :: Writer [String] a can be specialized with a ~ NUM.

Related

How to create a monad using StateT, ContT, and ReaderT?

How do I create a monad which uses State, Cont, and Reader transformers? I would like to read an environment, and update/use state. However, I would also like to pause/interrupt the action. For example, if a condition is met, the state remains unchanged.
So far I have a monad using ReaderT and StateT, but I cannot work out how to include ContT:
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Test where
-- monads
import Data.Functor.Identity (Identity, runIdentity)
import Control.Monad.State
import Control.Monad.Reader
import Control.Monad.Cont
-- reader environment
type In = Integer
-- cont: if true then pause, else continue
type Pause = Bool
-- state environment:
newtype StateType = StateType { s :: Integer }
newtype M r = M {_unM :: ReaderT In (ContT Pause (StateT StateType Identity)) r}
deriving ( Functor, Applicative, Monad
, MonadReader In
, MonadCont Pause
, MonadState StateType
)
-- run monadic action
runM :: In -> Pause -> StateType -> M r -> StateType
runM inp pause initial act
= runIdentity -- unwrap identity
$ flip execStateT initial -- unwrap state
$ flip runContT pause -- unwrap cont
$ flip runReaderT inp -- unwrap reader
$ _unM act -- unwrap action
This gives the error:
* Expected kind `* -> *', but `Pause' has kind `*'
* In the first argument of `MonadCont', namely `Pause'
In the newtype declaration for `M'
|
24| , MonadCont Pause
|
Ok, but why does Pause need kind * -> *?... I'm drowning in types, in need of explanation. What form does Pause have to take, a function? How does ContT integrate? Ultimately, I plan to use Cont for a control structure.
Unlike MonadReader and MonadState, the MonadCont type class takes only one parameter. Since that parameter m must be a Monad, it must have kind * -> *.
In your deriving clause, you want MonadCont, not MonadCont Pause.
added in response to followup question:
ContT is defined as:
newtype ContT r m a = ContT { runContT :: (a -> m r) -> m r }
Note that the r in your definition of newtype M r is passed as the final (a) parameter to ContT. Plugging in the variables, you have
ContT Bool (State StateType) a = ContT {
runContT :: (a -> State StateType Bool) -> (State StateType Bool)
}
This provides a computational context in which you can manipulate the StateType, and use delimited continuations. Eventually, you will construct a ContT Bool (State StateType) Bool. Then you can run the continuation (with evalContT), and return to the simpler State StateType context. (In practice, you may unwrap all 3 of your monad transformers in the same part of your program.)

Modifying inner reader in a transformer stack

I'm pulling together code from a number of different places, and I'm trying to deal with the following:
Problem
I have a transformer stack with the following simplified type:
action :: m (ReaderT r IO) a
and I'm trying to use the action in the context of a different stack, which has a different reader environment:
desired :: m (ReaderT r' IO) a
I can of course provide
f :: r' -> r
Example
things :: m (ReaderT r' IO) ()
things = do
-- ... some stuff
-- <want to use action here>
action :: m (ReaderT r IO) a -- broken
-- ... more stuff
pure ()
What I've considered
withReaderT :: (r' -> r) -> ReaderT r m a -> ReaderT r' m a
This has the problem that ReaderT is the outer monad, whilst I want to use it on an inner one.
I've also considered that this might be related to MonadBase or MonadTransControl, but I'm not familiar with their workings.
I don't think it's possible to write a function with signature:
changeReaderT :: (MonadTrans m)
=> (r -> r')
-> m (ReaderT r IO) a
-> m (ReaderT r' IO) a
the issue being that the only operation possible, in general, on the second argument is lifting it to t (m (ReaderT r IO)) a for some monad transformer t, which doesn't buy you anything.
That is, the MonadTrans m constraint alone doesn't provide enough structure to do what you want. You either need m to be an instance of a typeclass like MFunctor in the mmorph package that allows you to modify an inner layer of the monad stack in a general way by providing a function like:
hoist :: Monad m => (forall a. m a -> n a) -> t m b -> t n b
(which is what #Juan Pablo Santos was saying), or else you need an ability to dig into the structure of your m monad transformer to partially run and rebuild it (which will be transformer-specific).
The first approach (using hoist from the mmorph package) will be most convenient if your m is already made up of transformers supported by the mmorph package. For example, the following typechecks, and you don't have to write any instances:
type M n = MaybeT (StateT String n)
action :: M (ReaderT Double IO) a
action = undefined
f :: Int -> Double
f = fromIntegral
desired :: M (ReaderT Int IO) a
desired = (hoist $ hoist $ withReaderT fromIntegral) action
You'll need a hoist for each layer in M.
The second approach avoids hoist and requisite MFunctor instances but requires tailoring to your specific M. For the above type , it looks something like:
desired' :: M (ReaderT Int IO) a
desired' = MaybeT $ StateT $ \s ->
(withReaderT fromIntegral . flip runStateT s . runMaybeT) action
You basically need to run the monad down to the ReaderT layer and then rebuild it back up, treating layers like StateT with care. This is exactly what the MFunctor instances in mmorph are doing automatically.

How to inject the result of an IO action into a non-IO monadic computation

I have a small bit of an architectural problem for which I'd like to see if there's a common pattern or abstraction that can help me. I'm writing a game engine where the user is able to specify a game loop as a monadic computation of the form:
gameLoop :: TimeStep -> a -> Game a
where the Game monad has a bunch of access points for drawing, transforming, and interfacing with the engine in general. Then, I also provide a function that the user calls to run the simulation
runGame :: (TimeStep -> a -> Game a) -> a -> IO a
One of the main design goals of the library was to not make Game an instance of the MonadIO typeclass. This is to prevent the user from shooting themselves in the foot by changing the state of the underlying graphics calls, or loading things when they're not expected. However, there are often use cases where the result of an IO a is useful after the game loop has already begun. In particular, spawning enemies with procedurally generated graphical elements comes to mind.
As a result, I'd like to allow the user to request resources using something similar to the following interface:
data ResourceRequestResult a
= NotLoaded
| Loaded a
newtype ResourceRequest a = ResourceRequest {
getRequestResult :: Game (ResourceRequestResult a)
}
requestResource :: IO a -> Game (ResourceRequest a)
With this, I'd like to fork a thread to load the resource and pass the result to the context of the Game monad and back to the user. The main goal would be that I get to decide when the IO action takes place -- somewhere that I expect it to rather than in the middle of the game loop.
One idea that I had in mind was to place another user-defined monad transformer on top of the Game monad... something like
newtype ResourceT r m a = ResourceT (StateT [ResourceRequest r] m a)
However, I believe that then specifying things in terms of f :: ResourceT r Game a becomes an API nightmare, as I'd have to support any possible combination of monad transformer stacks. Ideally I'd also like to avoid making Game polymorphic in r, as it would increase the verbosity and portability of the underlying Game functions as well.
Does Haskell have any abstractions or idioms for something like this programming pattern? Is what I want not possible?
The simplest thing is to use module-level encapsulation. Something like this:
module Game (Game, loadResource) where
data GameState -- = ...
newtype Game = Game { runGame :: StateT GameState IO a }
io :: IO a -> Game a
io = Game . liftIO
loadResource :: IO a -> Game (Game a)
loadResource action = io $ do
v <- newEmptyMVar
forkIO (action >>= putMVar v)
return . io $ takeMVar v
As seen here, you can use the fact that Game can do IO within the Game module without exposing this fact to the rest of the world, exposing only the bits of IO that you consider "safe". In particular, you would not make Game an instance of MonadIO (and it can't be made an instance of MonadTrans as it has the wrong kind). Moreover, the io function and Game constructor are not exported, so the user can't pull an end-run in that way.
Monads and especially monad transformers come from trying to build complicated programs out of simpler pieces. An additional transformer for the new responsibility is an idiomatic way of handling this problem in Haskell.
There's more than one way to deal with transformer stacks. Since you are already using mtl in your code, I'll assume you are comfortable with the choice of typeclasses for penetrating the transformer stack.
The examples given below are complete overkill for the toy problem. This whole example is huge - it shows how pieces can come together from monads defined in multiple different ways - in terms of IO, in terms of a transformer like RWST and in terms of free monad from a functor.
An interface
I like complete examples, so we'll start with a complete interface for a game engine. This will be a small collection of typeclasses each representing one responsibility of the game engine. The ultimate goal will be to provide a function with the following type
{-# LANGUAGE RankNTypes #-}
runGame :: (forall m. MonadGame m => m a) -> IO a
As long as MonadGame doesn't include MonadIO a user of runGame can't make use of IO in general. We can still export all of our underlying types and write instances like MonadIO and a user of the library can still be sure they didn't make a mistake as long as they enter the library through runGame. The typeclasses presented here are actually the same as a free monad, and you don't have to choose between them.
If you don't like either the rank 2 type or a free monad for some reason, you can instead make a new type with no MonadIO instance and not export the constructor as in Daniel Wagner's answer.
Our interface will consist of four type classes - MonadGameState for handling state, MonadGameResource for handling resources, MonadGameDraw for drawing, and an overarching MonadGame that includes all the other three for convenience.
The MonadGameState is a simpler version of MonadRWS from Control.Monad.RWS.Class. The only reason to define our own class is so that MonadRWS is still available for someone else to use. MonadGameState needs data types for the games configuration, how it outputs data to draw, and the state maintained.
import Data.Monoid
data GameConfig = GameConfig
newtype GameOutput = GameOutput (String -> String)
instance Monoid GameOutput where
mempty = GameOutput id
mappend (GameOutput a) (GameOutput b) = GameOutput (a . b)
data GameState = GameState {keys :: Maybe String}
class Monad m => MonadGameState m where
getConfig :: m GameConfig
output :: GameOutput -> m ()
getState :: m GameState
updateState :: (GameState -> (a, GameState)) -> m a
Resources are handled by returning an action that can be run later to get the resource if it was loaded.
class (Monad m) => MonadGameResource m where
requestResource :: IO a -> m (m (Maybe a))
I'm going to add another concern to the game engine and eliminate the need for a (TimeStep -> a -> Game a). Instead of drawing by returning a value, my interface will draw by asking for it explicitly. The return of draw will tell us the TimeStep.
data TimeStep = TimeStep
class Monad m => MonadGameDraw m where
draw :: m TimeStep
Finally, MonadGame will require instances for the other three type classes.
class (MonadGameState m, MonadGameDraw m, MonadGameResource m) => MonadGame m
Default definitions for transformers
It's easy to provide default definition of all four type classes for monad transformers. We'll add defaults to all three classes.
{-# LANGUAGE DefaultSignatures #-}
class Monad m => MonadGameState m where
getConfig :: m GameConfig
output :: GameOutput -> m ()
getState :: m GameState
updateState :: (GameState -> (a, GameState)) -> m a
default getConfig :: (MonadTrans t, MonadGameState m) => t m GameConfig
getConfig = lift getConfig
default output :: (MonadTrans t, MonadGameState m) => GameOutput -> t m ()
output = lift . output
default getState :: (MonadTrans t, MonadGameState m) => t m GameState
getState = lift getState
default updateState :: (MonadTrans t, MonadGameState m) => (GameState -> (a, GameState)) -> t m a
updateState = lift . updateState
class (Monad m) => MonadGameResource m where
requestResource :: IO a -> m (m (Maybe a))
default requestResource :: (Monad m, MonadTrans t, MonadGameResource m) => IO a -> t m (t m (Maybe a))
requestResource = lift . liftM lift . requestResource
class Monad m => MonadGameDraw m where
draw :: m TimeStep
default draw :: (MonadTrans t, MonadGameDraw m) => t m TimeStep
draw = lift draw
I know that I plan on using RWST for state, IdentityT for resources, and FreeT for drawing, so we'll provide instances for all of those transformers now.
import Control.Monad.RWS.Lazy
import Control.Monad.Trans.Free
import Control.Monad.Trans.Identity
instance (Monoid w, MonadGameState m) => MonadGameState (RWST r w s m)
instance (Monoid w, MonadGameDraw m) => MonadGameDraw (RWST r w s m)
instance (Monoid w, MonadGameResource m) => MonadGameResource (RWST r w s m)
instance (Monoid w, MonadGame m) => MonadGame (RWST r w s m)
instance (Functor f, MonadGameState m) => MonadGameState (FreeT f m)
instance (Functor f, MonadGameDraw m) => MonadGameDraw (FreeT f m)
instance (Functor f, MonadGameResource m) => MonadGameResource (FreeT f m)
instance (Functor f, MonadGame m) => MonadGame (FreeT f m)
instance (MonadGameState m) => MonadGameState (IdentityT m)
instance (MonadGameDraw m) => MonadGameDraw (IdentityT m)
instance (MonadGameResource m) => MonadGameResource (IdentityT m)
instance (MonadGame m) => MonadGame (IdentityT m)
Game state
We plan on building the game state from RWST, so we'll make GameT a newtype for RWST. This allows us to attach our own instances like MonadGameState. We'll derive as many classes as we can with GeneralizedNewtypeDeriving.
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
-- Monad typeclasses from base
import Control.Applicative
import Control.Monad
import Control.Monad.Fix
-- Monad typeclasses from transformers
import Control.Monad.Trans.Class
import Control.Monad.IO.Class
-- Monad typeclasses from mtl
import Control.Monad.Error.Class
import Control.Monad.Cont.Class
newtype GameT m a = GameT {getGameT :: RWST GameConfig GameOutput GameState m a}
deriving (Alternative, Monad, Functor, MonadFix, MonadPlus, Applicative,
MonadTrans, MonadIO,
MonadError e, MonadCont,
MonadGameDraw)
We'll also provide the underivable instance for MonadGameResource and a convenience function equivalent to runRWST
instance (MonadGameResource m) => MonadGameResource (GameT m)
runGameT :: GameT m a -> GameConfig -> GameState -> m (a, GameState, GameOutput)
runGameT = runRWST . getGameT
This lets us get to the meat of providing MonadGameState which just passes everything off onto RWST.
instance (Monad m) => MonadGameState (GameT m) where
getConfig = GameT ask
output = GameT . tell
getState = GameT get
updateState = GameT . state
If we just added MonadGameState to something that already provided support for resources and drawing we just made a MonadGame.
instance (MonadGameDraw m, MonadGameResource m) => MonadGame (GameT m)
Resource handling
We can handle resources with IO and MVars as in jcast's answer. We'll make a transformer just so we have a type to attach an instance for MonadGameResource to. This is total overkill. To add overkill to overkill, I'm going to newType IdentityT just to get its MonadTrans instance. We'll derive everything we can.
newtype GameResourceT m a = GameResourceT {getGameResourceT :: IdentityT m a}
deriving (Alternative, Monad, Functor, MonadFix, Applicative,
MonadTrans, MonadIO,
MonadError e, MonadReader r, MonadState s, MonadWriter w, MonadCont,
MonadGameState, MonadGameDraw)
runGameResourceT :: GameResourceT m a -> m a
runGameResourceT = runIdentityT . getGameResourceT
We'll add an instance for MonadGameResource. This is exactly the same as the other answers.
gameResourceIO :: (MonadIO m) => IO a -> GameResourceT m a
gameResourceIO = GameResourceT . IdentityT . liftIO
instance (MonadIO m) => MonadGameResource (GameResourceT m) where
requestResource a = gameResourceIO $ do
var <- newEmptyMVar
forkIO (a >>= putMVar var)
return (gameResourceIO . tryTakeMVar $ var)
If we just added resource handling to something that already supported drawing and state, we have a MonadGame
instance (MonadGameState m, MonadGameDraw m, MonadIO m) => MonadGame (GameResourceT m)
Drawing
Like Gabriel Gonzales pointed out, "You can purify any IO interface mechanically". We'll use this trick to implement MonadGameDraw. The only drawing operation is to Draw with a function from the TimeStep to what to do next.
newtype DrawF next = Draw (TimeStep -> next)
deriving (Functor)
Combined with the free monad transformer, this is the trick I'm using to eliminate the need for a (TimeStep -> a -> Game a). Our DrawT transformer that adds drawing responsibility to a monad with FreeT DrawF.
newtype DrawT m a = DrawT {getDrawT :: FreeT DrawF m a}
deriving (Alternative, Monad, Functor, MonadPlus, Applicative,
MonadTrans, MonadIO,
MonadError e, MonadReader r, MonadState s, MonadWriter w, MonadCont,
MonadFree DrawF,
MonadGameState)
Once again we'll define the default instance for MonadGameResource and another convenience function.
instance (MonadGameResource m) => MonadGameResource (DrawT m)
runDrawT :: DrawT m a -> m (FreeF DrawF a (FreeT DrawF m a))
runDrawT = runFreeT . getDrawT
The MonadGameDraw instance says we need to Free (Draw next) where the next thing to do is return the TimeStamp.
instance (Monad m) => MonadGameDraw (DrawT m) where
draw = DrawT . FreeT . return . Free . Draw $ return
If we just added drawing to something that already handles state and resources, we have a MonadGame
instance (MonadGameState m, MonadGameResource m) => MonadGame (DrawT m)
The game engine
Drawing and the game state interact with each other - when we draw we need to get the output from the RWST to know what to draw. This is easy to do if GameT is directly under DrawT. Our toy loop is very simple; it draws the output and reads lines from the input.
runDrawIO :: (MonadIO m) => GameConfig -> GameState -> DrawT (GameT m) a -> m a
runDrawIO cfg s x = do
(f, s, GameOutput w) <- runGameT (runDrawT x) cfg s
case f of
Pure a -> return a
Free (Draw f) -> do
liftIO . putStr . w $ []
keys <- liftIO getLine
runDrawIO cfg (GameState (Just keys)) (DrawT . f $ TimeStep)
From this we can define running a game in IO by adding GameResourceT.
runGameIO :: DrawT (GameT (GameResourceT IO)) a -> IO a
runGameIO = runGameResourceT . runDrawIO GameConfig (GameState Nothing)
Finally, we can write runGame with the signature we've wanted from the beginning.
runGame :: (forall m. MonadGame m => m a) -> IO a
runGame x = runGameIO x
Example
This example requests the reverse of the last input after 5 seconds and displays everything that has data available each frame.
example :: MonadGame m => m ()
example = go []
where
go handles = do
handles <- dump handles
state <- getState
handles <- case keys state of
Nothing -> return handles
Just x -> do
handle <- requestResource ((threadDelay 5000000 >>) . return . reverse $ x)
return ((x,handle):handles)
draw
go handles
dump [] = return []
dump ((name, handle):xs) = do
resource <- handle
case resource of
Nothing -> liftM ((name,handle):) $ dump xs
Just contents -> do
output . GameOutput $ (name ++) . ("\n" ++) . (contents ++) . ("\n" ++)
dump xs
main = runGameIO example
You probably want to look up MVars: http://hackage.haskell.org/package/base-4.7.0.1/docs/Control-Concurrent-MVar.html.
tryReadMVar :: MVar a -> IO (Maybe a)
gives you your ResourceRequest, and
putMVar :: MVar a -> a -> IO ()
can be used to press the result at the end of the thread. Something like (ignoring newtypes etc.):
requestResourceImpl :: IO a -> IO (IO (Maybe a))
requestResourceImpl a = do
mv <- newEmptyMVar
forkIO $ do
x <- a
putMVar mv x
return $ tryReadMVar mv
This doesn't handle cases where a throws exceptions etc; if a does throw an exception, your resulting ResourceRequest will simply never report the resource as being available.
I strongly recommend making GameMonad an abstract type. You can make it a newtype (you can add deriving MonadReader etc. if necessary). Then you don't export its constructor; instead, define abstract operations like requestResource and export them instead.

Why use MultiParamTypeClasses in a MonadState

class Monad m => MonadState s m | m -> s where
-- | Return the state from the internals of the monad.
get :: m s
get = state (\s -> (s, s))
-- | Replace the state inside the monad.
put :: s -> m ()
put s = state (\_ -> ((), s))
-- | Embed a simple state action into the monad.
state :: (s -> (a, s)) -> m a
state f = do
s <- get
let ~(a, s') = f s
put s'
return a
instance MonadState s m => MonadState s (MaybeT m) where...
Why does a instance of MonadState need a state and a monad, why not create a single parameter State class?
Let me try and answer Gert's question in the comments, because it's a pretty different question.
The question is, why can we not just write
class State s where
get :: s
put :: s -> ()
Well, we could write this. But now the question is, what can we do with it? And the hard part is, if we have some code with put x and then later get, how do we link the get to the put so that the same value is returned as the one put in?
And the problem is, with just the types () and s, there is no way to link one to the other. You can try implementing it in various ways, but it won't work. There's just no way to carry the data from the put to the get (maybe someone can explain this better, but the best way to understand is to try writing it).
A Monad is not necessarily the only way to make operations linkable, but it is a way, because it has the >> operator to link two statements together:
(>>) :: m a -> m b -> m b
so we can write
(put x) >> get
EDIT: Here is an example using the StateT instance defined in the package
foo :: StateT Int IO ()
foo = do
put 3
x <- get
lift $ print x
main = evalStateT foo 0
You need some way of associating the type of the state to the type of the monad. MultiParamTypeClasses with FunctionalDependencies is one way. However, you can also do it using TypeFamilies.
class (Monad m) => MonadState m where
type StateType m
-- | Return the state from the internals of the monad.
get :: m (StateType m)
get = state (\s -> (s, s))
-- | Replace the state inside the monad.
put :: StateType m -> m ()
put s = state (\_ -> ((), s))
-- | Embed a simple state action into the monad.
state :: (StateType m -> (a, StateType m)) -> m a
state f = do
s <- get
let ~(a, s') = f s
put s'
return a
instance MonadState m => MonadState (MaybeT m) where
type StateType (MaybeT m) = StateType m
...
This is the approach taken by the monads-tf package.

Combine state with IO actions

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?
Use liftIO
You're already very close! Your suggestion
newtype Op a = Op {runOp :: ST -> IO (ST, a)}
is excellent and the way to go.
To be able to execute getLine in an Op context, you need to 'lift' the IO operation into the Op monad. You can do this by writing a function liftIO:
liftIO :: IO a -> Op a
liftIO io = Op $ \st -> do
x <- io
return (st, x)
You can now write:
runOp (do x <- liftIO getLine; ...
Use class MonadIO
Now the pattern of lifting an IO action into a custom monad is so common that there is a standard type class for it:
import Control.Monad.Trans
class Monad m => MonadIO m where
liftIO :: IO a -> m a
So that your version of liftIO becomes an instance of MonadIO instead:
instance MonadIO Op where
liftIO = ...
Use StateT
You've currently written your own version of the state monad, specialised to state ST. Why don't you use the standard state monad? It saves you from having to write your own Monad instance, which is always the same for the state monad.
type Op = StateT ST IO
StateT already has a Monad instance and a MonadIO instance, so you can use those immediately.
Monad transformers
StateT is a so-called monad transformer. You only want IO actions in your Op monad, so I've already specialized it with the IO monad for you (see the definition of type Op). But monad transformers allow you to stack arbitrary monads. This what intoverflow is talking about. You can read more about them here and here.
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 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.

Resources