Separating concerns in a DSL using MTL - haskell

I'm writing a small DSL using monad-transformers following the ideas presented
here here. For the sake of
illustration I present a small subset here.
class Monad m => ProjectServiceM m where
-- | Create a new project.
createProject :: Text -- ^ Name of the project
-> m Project
-- | Fetch all the projects.
getProjects :: m [Project]
-- | Delete project.
deleteProject :: Project -> m ()
The idea of this DSL is to be able to write API-level tests. To this end, all
these actions createProject, getProjects, deleteProject will be
implemented by REST calls to a web-service.
I also wrote an DSL to write expectations. An snippet is given below:
class (MonadError e m, Monad m) => ExpectationM e m | m -> e where
shouldContain :: (Show a, Eq a) => [a] -> a -> m ()
And you could imagine that more DSL's can be added to the mix for logging, and
performance metrics see the gist linked above.
With these DSL is possible to write some simple tests like the following:
createProjectCreates :: (ProjectServiceM m, ExpectationM e m) => m ()
createProjectCreates = do
p <- createProject "foobar"
ps <- getProjects
ps `shouldContain` p
Two interpreters are shown below:
newtype ProjectServiceREST m a =
ProjectServiceREST {runProjectServiceREST :: m a}
deriving (Functor, Applicative, Monad, MonadIO)
type Error = Text
instance (MonadIO m, MonadError Text m) => ProjectServiceM (ProjectServiceREST m) where
createProject projectName = return $ Project projectName
getProjects = return []
deleteProject p = ProjectServiceREST (throwError "Cannot delete")
newtype ExpectationHspec m a =
ExpectationHspec {runExpectationHspec :: m a}
deriving (Functor, Applicative, Monad, MonadIO)
instance (MonadError Text m, MonadIO m) => ExpectationM Text (ExpectationHspec m) where
shouldContain xs x = if any (==x) xs
then ExpectationHspec $ return ()
else ExpectationHspec $ throwError msg
where msg = T.pack (show xs) <> " does not contain " <> T.pack (show x)
Now to run the scenario createProjectCreates the monad transformers can be
stacked in different ways. One way I found it makes sense is:
runCreateProjectCreates :: IO (Either Text ())
runCreateProjectCreates = ( runExceptT
. runExpectationHspec
. runProjectServiceREST
) createProjectCreates
Which requires:
instance ProjectServiceM (ProjectServiceREST (ExpectationM (ExceptT Text IO)))
instance ExpectationM Text (ProjectServiceREST (ExpectationM (ExceptT Text IO)))
The problem with this is that either the instances of ProjectSeviceM have to
know about ExpectationM and create instances for it, or vice-versa. These
instances can be readily created by using the StandaloneDeriving extension, e.g.:
deriving instance (ExpectationM Text m) => ExpectationM Text (ProjectServiceREST m)
However it'd be nice if this could be avoided, since I'm leaking some
information to either implementations of the DSL's. Can the problem above be
overcome?

The concrete constructors for your monad stack don't have to correspond directly to the mtl-style type classes. This article and Reddit discussion are relevant. The mtl class MonadState s m has a generic dumb implementation in StateT, but you can instantiate MonadState for ReaderT (IORef s) IO too, or for a CPS variant. Ultimately, you remain abstract in how you want the effect to be handled, you just require that it is handled.
Suppose instead you wrote two abstract monad transformers:
newtype ProdT m a = ProdT { runProdT :: ... }
deriving (Functor, Applicative, Monad, MonadTrans, ...)
newtype TestT m a = TestT { runTestT :: ... }
deriving (Functor, Applicative, Monad, MonadTrans, ...)
and you then define the instances you require. Instead of needing to write all the pass through instances, you can just write the ones you need directly.
As an aside, I would recommend not defining type classes if they are trivial combinations of other classes. The class/instance definitions for
class (MonadError e m, Monad m) => ExpectationM e m | m -> e where
shouldContain :: (Show a, Eq a) => [a] -> a -> m ()
works just as well as
shouldContain :: (MonadError e m, Show a, Eq a) => [a] -> a -> m ()
You already have the ability to alter the base monad, as long as it has MonadError. A test implementation would possibly be
newtype ExpectationT m e a = ExpectationT { runExpectation :: WriterT [e] m a }
instance Monad m => MonadError (ExpectationT m e) e where
throwError = ExpectationT . tell
-- etc..

Related

Best way to combine multiple state/reader monads?

I am writing a project that involves composing several stacks of StateT and ReaderT monads:
newtype FooT m a = FooT { unFooT :: (StateT State1 (ReaderT Reader1 m)) a }
newtype BarT m a = BarT { unBarT :: (StateT State2 (ReaderT Reader2 m)) a }
Then, I basically just run everything in FooT (BarT m) and lift into the appropriate monad as necessary. I'm using lens to interact with the various state/reader types:
foo :: Monad m => FooT m ()
foo = do
field1 .= ... -- where field1 is a lens into State1
...
However, this approach gets ugly as I add more StateT + ReaderT transformers (and seems like it might incur some performance costs).
My only idea so far is to combine the states like:
newtype BazT m a = BazT { unBazT :: StateT (State1, State2) (ReaderT (Reader1, Reader2) m)) a }
and then I can just project into the state types with more lenses.
foo :: Monad m => BazT m ()
foo = do
(_1 . field1) .= ... -- where field1 is a lens into State1
...
Is there a canonical way to combine multiple states like this? If possible I'd like to avoid modifying all the lens code.
If you happen to work on both states in the same context a lot, you should combine these states, as they fit to one functionality.
A stack is typically encapsulating one functionality at a time. So, within one stack, you would generally need every monad transformer at most once.
To encapsulate a stack, you'll have to make sure that inner transformers are not exposed outside. That way, your stacks can be combined further.
Complete example on how to encapsulate a monad stack:
{-# LANGUAGE UndecidableInstances #-} -- Needed for all things around transformers.
import Control.Monad.Reader
import Control.Monad.State
import Control.Monad.Writer
import Control.Monad.Except
import Control.Applicative
data Reader1 = Reader1
data State1 = State1
newtype FooT m a = FooT { unFooT :: (StateT State1 (ReaderT Reader1 m)) a }
deriving
( Functor, Applicative, Monad, Alternative
, MonadWriter w, MonadError e, MonadIO
-- ..
)
-- Note that Reader and State are not derived automatically.
-- Instead, the instances are lifted past its inside manually.
instance MonadTrans FooT where
lift = FooT . lift . lift
instance MonadState s m => MonadState s (FooT m) where
get = lift $ get
put = lift . put
instance MonadReader r m => MonadReader r (FooT m) where
ask = lift $ ask
local f = mapFooT $ mapStateT $ mapReaderT $ local f
where
mapFooT g = FooT . g . unFooT
-- Your class that provides the functionality of the stack.
class Monad m => MonadFoo m where
fooThings :: m Reader1
-- ...
-- Access to the inside of your stack, to implement your class.
instance Monad m => MonadFoo (FooT m) where
fooThings = FooT $ ask
-- Boilerplate to include all the typical transformers,
-- so that MonadFoo can be accessed and derived through them.
instance MonadFoo m => MonadFoo (ReaderT r m) where
fooThings = lift $ fooThings
instance MonadFoo m => MonadFoo (StateT s m) where
fooThings = lift $ fooThings
-- ..... instances for all the other common transformers go here ..
-- Another stack, that can now derive MonadFoo.
data Reader2 = Reader2
data State2 = State2
newtype BarT m a = BarT { unBarT :: (StateT State2 (ReaderT Reader2 m)) a }
deriving
( Functor, Applicative, Monad, Alternative
, MonadWriter w, MonadError e, MonadIO
, MonadFoo
)
-- Bar class and related instances would follow here as before.
-- A new stack that can make use of Bar and Foo, preferably through their classes.
newtype BazT m a = BazT { unBazT :: BarT (FooT m) a }
-- Baz can have its own ReaderT and StateT transformers,
-- without interfering with these in FooT and BarT.
As you can see, it requires quite a lot of boilerplate code. You may omit some boilerplate, if its for internal code. If you write a library, your users will appreciate it though.
Various packages tackle the boilerplate issue.

Replicating the concept of liftIO with a monadtransformer

I have trouble even explaining what I'm trying to do, please bear with me. I haven't really grasped type theory, or how to use forall so that is probably the reason why wording (and implementing) this is difficult for me. So basically I'm trying to replicate the behaviour of MonadIO with a MonadTransformer. MonadIO is defined as
class (Monad m) => MonadIO m where
-- | Lift a computation from the 'IO' monad.
liftIO :: IO a -> m a
-- | #since 4.9.0.0
instance MonadIO IO where
liftIO = id
This code compiles (I haven't included the Applicative and Functor instances here though, but I can):
newtype Debugable m r = Debugable { runDebugable :: (DebugState -> m (DebugState, r)) }
instance MonadTrans (Debugable) where
lift action = Debugable $ \d -> do
r <- action
return (d, r)
class (Monad i, Monad m) => MonadDebug i m where
liftDebug :: Debugable i a -> m a
instance (Monad m) => MonadDebug m (Debugable m) where
liftDebug = id
I want to write a method like this, that should work in any MonadTransformer above MonadDebug in the transformer stack:
debug :: (MonadDebug i m) => String -> m ()
debug msg = liftDebug $ doDebug msg
(doDebug operates in the Debugable monad)
Of course this does not compile because the type variable i cannot be deduced (or something like that)... I tried a few things, but I don't really understand what the debug messages mean either. I don't even know if a generalized lifting of a MonadTransformer similar to liftIO is possible... Is it?
I suggest changing your goal slightly.
The idiomatic way to make this class is to expose all of the base transformer's operations as class methods, rather than exposing a lifting transformation. Like this:
class Monad m => MonadDebugable m where
debug :: (DebugState -> (DebugState, r)) -> m r
instance Monad m => MonadDebugable (Debugable m) where
debug f = Debugable (pure . f)
instance MonadDebugable m => MonadDebugable (ReaderT r m) where
debug = lift . debug
-- and a dozen other instances for other transformers
Then you can offer derived operations, like, say,
observeCurrentState :: MonadDebugable m => m DebugState
observeCurrentState = debug (\s -> (s, s))
recordState :: MonadDebugable m => DebugState -> m ()
recordState s = debug (\_ -> (s, ()))
debugMsg :: MonadDebugable m => String -> m ()
debugMsg msg = debug (\s -> (s { messages = msg : messages s }, ())) -- or whatever
Then, in all your client code, where you would originally have written liftDebug foo, you simply write foo (but using operations like those above that are polymorphic over which exact monad stack you're using at the moment).
Of course, the obvious follow-up question is: what's the deal with MonadIO, then? Why doesn't IO follow this pattern? The answer there is that IO just has too many primitive operations to write a sensible class -- indeed, with the FFI, it's even possible for libraries and users to add new primitives. So we have to go with the slightly less desirable solution there, where we must annotate all our primitive calls with liftIO if we're not exactly in IO already. Skipping the annotations, like above, would be nicer, but we just can't have it.

Type constraints for polymorphic functions like lift

So I have this code
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
import MonadA
data A = A
newtype MonadA a => MyStateT a b { runMyStateT :: StateT A a b }
deriving (Functor, Applicative, Monad, MonadIO, MonadState A)
instance MonadTrans MyStateT where
lift = MyStateT . lift
and I get the compiler complaining that it cannot prove the m from signature of lift is of type MonadA or thats how I read these cryptic error messages.
Could not deduce (MonadA m) arising from a use of `MyStateT'
from the context (Monad m)
bound by the type signature for
lift :: Monad m => m a -> MyStateT m a
is there a way to deal with this? I think I need the constraint to be able to instantiate as follows:
instance MonadA a => MonadA (MyStateT a) where
f = MyStateT . lift . f
Also would such an implementation of f work? (I did not get that far due to the above error). I hope f on the right side to resolve to f over the inner monad a.
Edit: It indeed helped to drop the type constraint in newtype MonadA a => MyStateT ... to avoid the exact error that I mentioned. However there was just another error which I previously attributed to the same thing, consider this continuation of the example code above (some parts are repeated, now without type constraints):
class MonadB m where
fB :: m ()
newtype MyStateT m a = MyStateT { runMyStateT :: StateT A m a}
deriving (... MonadState A ...)
instance MonadTrans MyStateT where
lift = MyStateT . lift
instance MonadA a => MonadA (MyStateT a) where
f = lift . f
instance MonadA a => MonadB (MyStateT a) where
fB = lift (modify (...))
the error is
Could not deduce (a ~ StateT A m0) from context (MonadA (MyStateT a), MonadA a)
in the implementation of fB. Earlier I tried class MonadA m => MonadB m to no avail. It doesn't even make sense to be matching a with StateT A m. Since MyStateT is an instance of MonadState A it should work, no?
Edit:
OK, got it working:
fB = MyStateT (modify ...)
stupid me.
The solution is dropping the constraint from the MyStateT definition:
newtype MyStateT a b { runMyStateT :: StateT A a b }
deriving (Functor, Applicative, Monad, MonadIO, MonadState A)
Datatype constraints are essentially useless, as you need to put them in the function signatures anyway. For that reason, that feature is actually deprecated.
I think I need the constraint to be able to instantiate as follows:
Not really; instance MonadA a => MonadA (MyStateT a) will work just fine without it.
Also would such an implementation of f work?
It will. Note that, as you provided a MonadTrans instance for MyStateT, you don't actually need to wrap with MyStateT explicitly:
instance MonadA a => MonadA (MyStateT a) where
f = lift . f

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.

Resources