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.
Related
In https://en.wikibooks.org/wiki/Haskell/Monad_transformers, I see
newtype MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) }
and then the instanciation of MaybeT as a monad:
instance Monad m => Monad (MaybeT m) where
return = MaybeT . return . Just
-- The signature of (>>=), specialized to MaybeT m:
-- (>>=) :: MaybeT m a -> (a -> MaybeT m b) -> MaybeT m b
x >>= f = MaybeT $ do maybe_value <- runMaybeT x
case maybe_value of
Nothing -> return Nothing
Just value -> runMaybeT $ f value
I do not understand the arg x in runMaybeT x. Shouldn't runMaybeT take an arg corresponding to a? But instead it is given x which is the entire MonadT monad
It's just standard record syntax. In a type definition such as
data Foo = Foo { a :: Int, b :: String }
it presumably does not surprise you that we automatically get functions a :: Foo -> Int and b :: Foo -> String.
It's absolutely no different for MaybeT. (It does use newtype rather than data but that doesn't matter here, the same would be true if the definition used data instead)
newtype MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) }
this gives us a function runMaybeT :: MaybeT m a -> m (Maybe a) for exactly the same reason that we get the functions a and b in the simpler example above. It's just what record syntax does.
Specifically in this case, as always with a newtype defined using record syntax, the runMaybeT function allows us to "unwrap" a monadic value, going from the abstract MaybeT m a to the more "concrete" representation as a value of type m (Maybe a).
This is used in the implementation of >>= for MaybeT m: since its first argument (x in your snippet) is of type MaybeT m a, we first need runMaybeT to get the "unwrapped" value of type m (Maybe a), then use the >>= of the "base monad" m (shown here with do notation) to "extract" a value of type Maybe a which is then pattern matched on in the usual way.
runMaybeT is a getter. It is a function that takes a MaybeT m a object, and returns an m (Maybe a) object, so it has signature MaybeT m a -> m (Maybe a).
Perhaps it is easier to understand however if we unpack the value with a pattern:
instance Monad m => Monad (MaybeT m) where
return = MaybeT . return . Just
MaybeT rm >>= f = MaybeT $ do
maybe_value <- rm
case maybe_value of
Nothing -> return Nothing
Just value -> runMaybeT $ f value
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.
Working in an IO computation I ended up with a staircase of case mbValue of …s and figured out that I should use the Maybe monad to simplify the code. Since it's within an IO computation and I need to get IO values, I used the MaybeT monad transformer so that I can lift IO computation into Maybe.
Now, I have always thought about values being “stripped” of their Maybeness after an values <- mbValue inside a Maybe computation, but this turns out to be too simple of a heuristic here.
As highlighted below, when using a Maybe a value as an a (here by passing it to read), it fails to type check:
import Control.Monad.Trans (lift)
import Control.Monad.Trans.Maybe (runMaybeT)
lol :: IO (Maybe Int)
lol = return (Just 3)
lal :: IO (Maybe String)
lal = return (Just "8")
foo :: IO (Maybe Bool)
foo = do
b <- runMaybeT $ do
x <- lift lol
y <- lift lal
return (x < (read y))
return b ^-- Couldn't match type ‘Maybe String’ with ‘String’
main = foo >>= print
If I put a typed hole in for return (x < (read y)), I see that it expects a Bool, which makes sense, but also that the current bindings include
|| y :: Data.Maybe.Maybe GHC.Base.String
|| (bound at /private/tmp/test.hs:14:5)
|| x :: Data.Maybe.Maybe GHC.Types.Int
|| (bound at /private/tmp/test.hs:13:5)
I.e., y is a Maybe String. This of course explains the error, but I'm left confused. Where is my understanding wrong, and how can I fix this error?
In short: Replace lift by the MaybeT constructor.
Note that
newtype MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) }
and
lift :: (MonadTrans t, Monad m) => m a -> t m a
Your use of lift in
x <- lift lol
is at the type
lift :: IO (Maybe Int) -> MaybeT IO (Maybe Int)
That's why x will be a Maybe Int again. The lift adds a fresh MaybeT layer that is independent of the Maybe occurrence you already have.
But
MaybeT :: m (Maybe a) -> MaybeT m a
instead as in
x <- MaybeT lol
will be used at type
MaybeT :: IO (Maybe a) -> MaybeT IO a
and do the right thing.
When specialized to MaybeT, lift :: Monad m => m a -> MaybeT m a. Since lol :: IO (Maybe Int), m is IO and a is Maybe Int, therefore lift lol :: MaybeT IO (Maybe Int).
IO (Maybe a) is just the value contained within a MaybeT IO a newtype wrapper, so there's no need to lift it; instead use the MaybeT constructor, for example as in MaybeT lol.
But this is not how people tend to use monad transformers. Instead, just use MaybeT values and lift as needed:
import Control.Monad
import Control.Monad.Trans (lift)
import Control.Monad.Trans.Maybe (runMaybeT, MaybeT)
lol :: MaybeT IO Int
lol = return 3
lal :: MaybeT IO String
lal = return "8"
foo :: IO (Maybe Bool)
foo =
runMaybeT $ do
x <- lol
y <- lal
_ <- lift getLine -- lift (IO String) to MaybeT IO String
_ <- return 100 -- lift any pure value
_ <- mzero -- use the MonadPlus instance to get a lifted Nothing.
return (x < (read y))
main = foo >>= print
Have you already seen the following function? What is it called? What is it useful for? can it be defined more generically than just for StateT?
simpleFunction (StateT f) = StateT $ (\s -> return (f s, s))
By the way, ghc gives it the type Monad n => StateT s m a -> StateT s n (m (a, s)).
And there is an alternative definition:
simpleFunction m = do
s <- get
mapStateT (\l -> return (l, s)) m
Having two monads like n (m a) isn't usually very useful (the composition might not be a monad etc.). So I guess more usable version would be of type
(Monad m) => StateT s m a -> StateT s m (a, s).
This can be generalized to an arbitrary state transformer over m:
import Control.Monad
import Control.Monad.State
f1 :: (MonadState s (t m), MonadTrans t, Monad m) => StateT s m a -> t m (a, s)
f1 (StateT f) = get >>= lift . f
And since the only thing we need is get, we can further generalize to:
f2 :: (MonadTrans t, Monad m, Monad (t m)) => t m s -> StateT s m a -> t m (a, s)
f2 g (StateT f) = g >>= lift . f
If you really need to have two monads, perhaps MFunctor would be useful - functions like hoist or generalize allow to switch between monads.
The simpleFunction structure is parameterized over two things: a StateT monad and a function f. As it is shown in both given definitions, the simpleFunction gets the state from the internals of the monad, then map both the return value and final state of a computation using a given function.
Following a very simple example to see a working example:
import Control.Monad.State
inc :: State Int Int
inc = do
n <- get
put (n + 1)
return n
simpleFunction = do
s <- get
mapState (\l -> (l,s)) inc
main = do
print $ runState simpleFunction 1
The result is: ((1,2),1) which is the ((return value,final state),initial state)
The simpleFunction as you see is not complicated. It can be used with monads other than StateT. In this case, you have to implement your own map and get.
Hope it's useful!
I need to write a state monad that can also support error handling. I was thinking of using the Either monad for this purpose because it can also provide details about what caused the error. I found a definition for a state monad using the Maybe monad however I am unable to modify it to use Either, instead of Maybe. Here's the code:
newtype StateMonad a = StateMonad (State -> Maybe (a, State))
instance Monad StateMonad where
(StateMonad p) >>= k = StateMonad (\s0 -> case p s0 of
Just (val, s1) -> let (StateMonad q) = k val in q s1
Nothing -> Nothing)
return a = StateMonad (\s -> Just (a,s))
data State = State
{ log :: String
, a :: Int}
Consider using ExceptT from Control.Monad.Trans.Except (instead of using Either).
import Control.Monad.State
import Control.Monad.Trans.Except
import Control.Monad.Identity
data MyState = S
type MyMonadT e m a = StateT MyState (ExceptT e m) a
runMyMonadT :: (Monad m) => MyMonadT e m a -> MyState -> m (Either e a)
runMyMonadT m = runExceptT . evalStateT m
type MyMonad e a = MyMonadT e Identity a
runMyMonad m = runIdentity . runMyMonadT m
If you aren't comfortable with Monads and Monad transformers then I'd do that first! They are a huge help and programmer productivity performance win.
There are two possible solutions. The one that is closest to the code you provided above is:
newtype StateMonad e a = StateMonad (State -> Either e (a, State))
instance Monad (StateMonad e) where
(StateMonad p) >>= k =
StateMonad $ \s0 ->
case p s0 of
Right (val, s1) ->
let (StateMonad q) = k val
in q s1
Left e -> Left e
return a = StateMonad $ \s -> Right (a, s)
data State = State
{ log :: String
, a :: Int
}
The other form moves the error handling within the state handling:
newtype StateMonad e a = StateMonad (State -> (Either e a, State))
instance Monad (StateMonad e) where
(StateMonad p) >>= k =
StateMonad $ \s0 ->
case p s0 of
(Right val, s1) ->
let (StateMonad q) = k val
in q s1
(Left e, s1) -> (Left e, s1)
return a = StateMonad $ \s -> (Right a, s)
data State = State
{ log :: String
, a :: Int
}
You need a monad transformer. Monad transformer libraries such as mtl allow you to compose different monads to make a new version. Using mtl, you could define
type StateMonad e a = StateT State (Either e) a
which will allow you to access both state and error handling within your StateMonad.
I didn't see anyone here mention the paper Monad Transformers Step by Step by Martin Grabmüller
I found it to be very helpful in learning about combining monads.
Just saw examples like
type StateMonad e a = StateT State (Either e) a
and
type MyMonadT e m a = StateT MyState (ExceptT e m) a
but as far as I understand, you will lose your state in case of error, because here you add state inside Either/Except, so state will be only accessible in Right.
If you need handle error and get state, which was computed up to moment where error occurred, you can use ExceptT e (State s) a stack:
type StateExcept e s a = ExceptT e (State s) a
test :: Int -> StateExcept String String ()
test limit = do
modify (succ . head >>= (:)) -- takes first char from state and adds next one in alphabet to state
s <- get
when (length s == limit) (throwError $ "State reached limit of " ++ show limit)
runTest :: ExceptT String (State String) () -> (Either String (), [Char])
runTest se = runState (runExceptT se) "a"
λ: runTest (forever $ test 4)
(Left "State reached limit of 4","dcba")
λ: runTest (replicateM_ 2 $ test 4)
(Right (),"cba")
You can always use a ErrorT monad transformer with a State monad inside (or vice versa).
Have a look at the transformers section of all about monads.
HTH,