I'm trying to "resume" a monadic computation from within IO and fearing that I may be out of luck. The situation is the following:
ioBracketFoo :: (a - > IO b) -> IO b
withBar :: MonadIO m => (a -> m b) -> m b
withBar action = liftIO $ ioBracketFoo $ \foo -> runMagic (action f)
Basically I want to resume my (unknown) monadic computation from within ioBracketFoo. If it were not a bracketing function then I'd be able to get the resource using res <- liftIO getFoo and release it later, and I wouldn't have to resume my monadic computation from within IO.
Is there any other creative use of lift or similar to make this possible?
This problem is sloved by MonadBaseControl. MonadBaseControl provides the functions to store and restart a monadic computation. You'll require an additional dependency to MonadBaseControl, which will prevent unstorable monads from beeing used in your bracket-funciton, for example
There is a tutorial on fp-complete, that should answer all basic questions.
Related
I will preface this by saying that I am a novice Haskell programmer (tinkered with it sporadically over the years) but I have a fair few years on the counter when it comes to OOO and imperative programming. I am currently learning how to use monads & combining them via the use of monad transformers (assuming I've got the correct term).
While I am able to assemble/chain things together, I am finding it hard to build an intuition for what the best way and style is & how best to assemble/write those interactions.
Specifically, I am curious to know what the best practice (or at least your practice) is for using lift/liftIO and any flavour in between & if there is way (and benefit) to hide them as I am finding them rather 'noisy'.
Below is an example snippet which I have put together to illustrate what I mean :
consumeRenderStageGL' :: RenderStage -> StateT RenderStageContext IO ()
consumeRenderStageGL' r = do
pushDebugGroupGL (name r)
liftIO $ consumePrologueGL ( prologue r )
liftIO $ consumeEpilogueGL ( epilogue r )
consumeStreamGL ( stream r )
liftIO $ popDebugGroupGL
Some of the functions it calls make use of the state monad :
pushDebugGroupGL :: String -> StateT RenderStageContext IO ()
pushDebugGroupGL tag = do
currentDebugMessageID <- gets debugMessageID
liftIO $ GL.pushDebugGroup GL.DebugSourceApplication (GL.DebugMessageID currentDebugMessageID) tag
modify (\fc -> fc { debugMessageID = (currentDebugMessageID + 1) })
consumeStreamGL :: Stream -> StateT RenderStageContext IO ()
consumeStreamGL s = do
mapM_ consumeTokenGL s
logGLErrors
While most don't and just live in IO (meaning they have to be lifted):
consumePrologueGL :: Prologue -> IO ()
consumePrologueGL p = do
colourClearFlag <- setupAndReturnClearFlag GL.ColorBuffer ( clearColour p ) (\(Colour4 r g b a) -> GL.clearColor $= (GL.Color4 r g b a))
depthClearFlag <- setupAndReturnClearFlag GL.DepthBuffer ( clearDepth p ) (\d -> GL.clearDepthf $= d)
stencilClearFlag <- setupAndReturnClearFlag GL.StencilBuffer ( clearStencil p ) (\s -> GL.clearStencil $= fromIntegral s)
GL.clear $ catMaybes [colourClearFlag, depthClearFlag, stencilClearFlag]
logGLErrors
where
setupAndReturnClearFlag flag mValue function = case mValue of
Nothing -> return Nothing
Just value -> (function value) >> return (Just flag)
My question is : is there any way to hide the liftIO in consumeRenderStageGL' and more importantly would this be a good or a bad idea?
One way I can think of hiding/getting rid of the liftIO is to bring both my consumePrologueGL & consumeEpilogueGL into my state monad but this seems wrong in the sense that those function do not need (and should not) interact with it ; all this just to reduce code noise.
The other option I can think of is to simply create the lifted version of the functions and call them in consumeRenderStageGL' - this would reduce the code noise but be identical in execution/evaluation.
The third option, which is how my logGLErrors works is that I have used a type class which has an instance defined for both IO & my state monads.
I look forward to reading what your opinions, advices and practices are.
Thanks in advance!
There are a few solutions. A common one is to make your basic actions MonadIO m => m … instead of IO …:
consumePrologueGL :: (MonadIO m) => Prologue -> m ()
consumePrologueGL p = liftIO $ do
…
Then you can use them in StateT RenderStageContext IO () without wrapping, due to MonadIO m => MonadIO (StateT s m), and of course MonadIO IO where liftIO is the identity function.
You can also abstract over the StateT part using MonadState from mtl, so if you add another transformer above/below it, you won’t have the same issue of lifting from/to StateT.
pushDebugGroupGL
:: (MonadIO m, MonadState RenderStageContext m)
=> String -> m ()
Generally, a concrete stack of transformers types is fine, it just helps to wrap all your basic operations for convenience so that all the lifts are in one place.
mtl helps remove the lift noise from your code altogether, and working in a polymorphic type m means you have to declare which effects a function actually uses, and can substitute different implementations of all the effects (except for MonadIO) for testing. Using monad transformers as an effect system like this is great if you have few types of effects; if you want something finer-grained or more flexible, you’ll start hitting the pain points that make people reach for algebraic effects instead.
It’s also worth assessing whether you need StateT over IO. Typically if you’re in IO, you don’t need the pure state offered by StateT, so instead of StateT MutableState IO you might as well use ReaderT (IORef MutableState) IO.
It’s also possible to make that (or a newtype wrapper for it) an instance of MonadState MutableState, so your code using get/put/modify wouldn’t even need to change:
{-# Language GeneralizedNewtypeDeriving #-}
import Data.Coerce (coerce)
newtype MutT s m a = MutT
{ getMutT :: ReaderT (IORef s) m a }
deriving
( Alternative
, Applicative
, Functor
, Monad
, MonadIO
, MonadTrans
)
evalMutT :: MutT s m a -> IORef s -> m a
evalMutT = coerce
instance (MonadIO m) => MonadState s (MutT s m) where
state f = MutT $ do
r <- ask
liftIO $ do
-- NB: possibly lazier than you want.
(a, s) <- f <$> readIORef r
a <$ writeIORef r s
This combo of ReaderT & IO is a pretty common design pattern.
I have the following code snippet from internet:
calculateLength :: LengthMonad Int
calculateLength = do
-- all the IO operations have to be lifted to the IO monad in the monad stack
liftIO $ putStrLn "Please enter a non-empty string: "
s <- liftIO getLine
if null s
then throwError "The string was empty!"
else return $ length s
and could not understand, why the author use liftIO?
What is the purpose of liftIO?
It is defined as follows:
class (Monad m) => MonadIO m where
-- | Lift a computation from the 'IO' monad.
liftIO :: IO a -> m a
Is it possible to lift IO a -> [a]? It looks like natural transformation.
IO operations like getLine, putStrLn "..." only work inside the IO monad. Using them inside any other monad will trigger a type error.
Still, there are many monads M which are defined in terms of IO (e.g. StateT Int IO, and apparently your LengthMonad as well) and because of that they allow IO actions to be converted into M-actions, and executed as such.
However, we need a conversion for each M:
convertIOintoM1 :: IO a -> M1 a
convertIOintoM2 :: IO a -> M2 a
convertIOintoM3 :: IO a -> M3 a
...
Since this is cumbersome, the library defines a typeclass MonadIO having such conversion function, so that all the functions above can be named liftIO instead.
In practice, liftIO is used each time one wants to run IO actions in another monad, provided such monad allows for it.
I am not very good haskell programer. I am tasked to write a compiler at my university and I have choosen haskell, as it is good tool for that purpose. I am using monads StateT and ExcepT, so I have types:
type Runner r s = StateT s (ExceptT LatteError IO) r
type RT r s = IO (Either LatteError (r, s))
I use it create walk a program tree and produce asm code and it works as I want. As main function for walking over tree I use
rProgram :: Program -> Runner [String] CompileState
and to unpack result I use function
runR program = runExceptT (runStateT (rProgram program) initialCompileState)
What I want to do not is to have some validators, like type check, ident existance check, and I know to write them. Lets tell I have
tcProgram :: Program -> Runner () TypeCheckState
ieProgram :: Program -> Runner () IdentExistanceState
I want to run them both in elegant way, and an failure is indicated by throwError. How to put many functions like them together?
lens has a zoom combinator which uses a lens to "zoom in" on a part of the state.
zoom :: Lens' s t -> State t a -> State s a
(As is usual in lens, the actual type of zoom is more complicated than that, but this is a simple way to think about it.)
So we have
zoom _1 . tcProgram :: Program -> Runner () (TypeCheckState, t)
zoom _2 . ieProgram :: Program -> Runner () (s, IdentExistenceState)
and so
liftA2 (>>) (zoom _1 . tcProgram) (zoom _2 . ieProgram) :: Program -> Runner () (TypeCheckState, IdentExistenceState)
I'm making several API calls that are encapsulated in a type alias:
type ConnectT a = EitherT String (RWST ConnectReader ConnectWriter ConnectState IO) a
Here's a simplified version of a function which connects to two separate APIs:
connectBoth :: ConnectT ()
connectBoth = do
a <- connectAPI SomeAPI someFunction
b <- connectAPI OtherAPI otherFunction
connectAPI OtherAPI (b `sendTo` a)
The final call in connectBoth is very time sensitive (and the transactions are of a financial nature). I figure a and b could be evaluated in parallel, and with lazy IO I should be able to do this:
b <- a `par` connectAPI OtherAPI otherFunction
The documentation for par says that it Indicates that it may be beneficial to evaluate the first argument in parallel with the second.
Does this work with IO?
Can I get any more guaranteed than "it may be beneficial?"
Or if I want greater guarantees will I need to use an MVar and liftIO . forkIO?
If I evaluate a first, I think I can use eitherT to check if a succeeded. But if I evaluate both at the same time I get confused. Here is the situation:
If only a failed, I will retry a, if that fails I will run a function that manually reverses b
If only b failed, I will retry b, write to the log in RWS and return left
if both fail write to the log in RWS and return left
if both succeed process c (which is not as time sensitive as a or b)
But if I evaluate both in parallel, then how can I identify which one failed? If I use eitherT immediately after a then a will evaluate first. If I use it after b then I won't be able to tell which one failed.
Is there a way I can evaluate the IO calls in parallel but respond differently depending on which one (if any) fails? Or am I left with a choice of parallelism vs failure mitigation?
The solution you are looking for will use forkIO and MVars.
par
par is for multiprocessor parallelism, it helps evaluate terms in parallel. It doesn't help with IO. If you do
do
a <- (someProcess :: IO a)
...
By the time you reach ... everything from the IO action has happened (if we ignore evil lazy IO) to a point that a can be determined entirely by ordinary evaluation. This means that by the time you do b <- someOtherProcess, all of someProcess is already done. It's too late to do anything in parallel.
EitherT
You can explicitly examine the Either e a result of an EitherT e m a. runEitherT :: EitherT e m a -> m (Either e a) makes the success or failure explicit in the underlying monad. We can lift that right back into EitherT to make a computation that always succeeds (sometimes with an error) from one that sometimes fails.
import Control.Monad.Trans.Class
examine :: (MonadTrans t, Monad m) => EitherT e m a -> t m (Either e a)
examine = lift . runEitherT
forkIO
The simplest solution for doing two things in IO is forkIO. It starts another lightweight thread that you can forget about.
If you run a value with your transformer stack, there will be four pieces of data when you are done. The state ConnectState, the written ConnectWriter log, whether the computation was successful, and, depending on whether or not it was successful, either the value or the error.
EitherT String (RWST ConnectReader ConnectWriter ConnectState IO) a
^ ^ ^ ^ ^
If we write out the structure of this, it looks like
(RWST ConnectReader ConnectWriter ConnectState IO) (Either String a)
^ ^ ^ ^ ^
ConnectReader -> ConnectState -> IO (Either String a, ConnectState, ConnectWriter)
^ ^ ^ ^ ^
All four of those pieces of information end up in the result of the IO action. If you fork your stack, you need to decide what to do with all of them when you join the results back together. You have already decided that you want to explicitly handle the Either String a. The ConnectWriters can probably be combined together with <>. You will need to decide what to do with ConnectState.
We'll make a fork that returns all four of these pieces of data by shoving them into an MVar.
import Control.Concurrent
import Control.Concurrent.MVar
import Control.Monad.IO.Class
forkConnectT :: ConnectT a -> ConnectT (MVar (Either String a, ConnectState, ConnectWriter))
forkConnectT cta = do
result <- liftIO newEmptyMVar
r <- lift ask
s <- lift get
liftIO $ forkIO $ do
state <- runRWST (runEitherT cta) r s
putMVar result state
return result
Later, when we want the result, we can try and see if it is ready. We'll handle the Either for success and failure explicitly, while handling the state and writer behind the scenes.
import Data.Traversable
tryJoinConnectT :: MVar (Either String a, ConnectState, ConnectWriter) -> ConnectT (Maybe (Either String a))
tryJoinConnectT result = liftIO (tryTakeMVar result) >>= traverse reintegrate
Behind the scenes we reintegrate the ConnectWriter by telling this ConnectT to write what was accumulated in the other thread. You will need to decide what to do to combine the two states.
reintegrate :: (a, ConnectState, ConnectWriter) -> ConnectT a
reintegrate (a, s, w) = do
-- Whatever needs to be done with the state.
-- stateHere <- lift get
lift $ tell w
return a
If we want to wait until the result is ready, we can block reading the MVar. This offers less opportunity for handling errors such as timeouts.
joinConnectT :: MVar (Either String a, ConnectState, ConnectWriter) -> ConnectT (Either String a)
joinConnectT result = liftIO (takeMVar result) >>= reintegrate
Example
Putting it all together, we can fork a task in parallel, do something in this thread explicitly examining the success or failure, join with the result from the other thread, and reason about what to do next with explicit Eithers representing success or failure from each process.
connectBoth :: ConnectT ()
connectBoth = do
bVar <- forkConnectT $ connectAPI OtherAPI otherFunction
a <- examine $ connectAPI SomeAPI someFunction
b <- joinConnectT bVar
...
Going farther
If you are paranoid, you will also want to handle exceptions (some of which can be handled by forkFinally) and asynchronous exceptions. You will need to decide whether to bundle these exceptions into your stack or treat IO like it can always throw exceptions.
Consider using async instead of forkIO and MVars.
monad-control (which you already have dependencies on via either) provides mechanisms for building up, one transformer at a time, the type that represents the state of a monad transformer stack. We wrote this by hand as (Either String a, ConnectState, ConnectWriter). If you are going to grow your transformer stack, you might want to get this from MonadTransControl instead. You can restore the state from the forked thread(see MonadBaseControl section) in the parent to inspect it. You will still need to decide how to deal with the data from the two states..
I've got a bunch of stateful functions inside a State monad. At one point in the program there needs to be some IO actions so I've wrapped IO inside a StateT getting a pair of types like this:
mostfunctions :: State Sometype a
toplevel :: StateT Sometype IO a
To keep things simple I don't want pass the IO context into the main set of functions and I would like to avoid wrapping them in the monad stack type. But in order to call them from the toplevel function I need something akin to a lift, but I'm not trying to lift a value from the inner monad. Rather I want to convert the state in the StateT monad into something equivalent in the State monad. To do this I've got the following:
wrapST :: (State Sometype a) -> StateT Sometype IO a
wrapST f = do s <- get
let (r,s2) = runState f s
put s2
return r
This then get used to interleave things like the following:
toplevel = do liftIO $ Some IO functions
wrapST $ Some state mutations
liftIO $ More IO functions
....
It seems like a fairly obvious block of code so I'm wondering does this function have a standard name, and it is already implemented somewhere in the standard libraries? I've tried to keep the description simple but obviously this extends to pulling one transformer out of a stack, converting the wrapped value to the cousin of the transformer type, skipping the monads below in the stack, and then pushing the results back in at the end.
It may be a good idea to refactor your code to use the type StateT SomeType m a instead of State SomeType a, because the first one is compatible to an arbitrary monad stack. If you'd change it like this, you don't need a function wrapST anymore, since you can call the stateful functions directly.
Okay. Suppose you have a function subOne :: Monad m => State Int Int:
subOne = do a <- get
put $ a - 1
return a
Now, change the types of all functions like this one from State SomeType a to StateT SomeType m a, leaving m as is. This way, your functions can work on any monadic stack. For those functions, that require IO, you can specify, that the monad at the bottom must be IO:
printState :: MonadIO m => StateT Int m ()
printState = do a <- get
liftIO $ print a
Now, it should be possible to use both functions together:
-- You could use me without IO as well!
subOne :: Monad m => StateT Int m ()
subOne = do a <- get
put $ a - 1
printState :: MonadIO m => StateT Int m ()
printState = do a <- get
liftIO $ print a
toZero :: StateT Int IO ()
toZero = do subOne -- A really pure function
printState -- function may perform IO
a <- get
when (a > 0) toZero
PS: I use GHC 7, some of the libs changed midway, so it might be a bit different on GHC 6.
A more direct answer to your question: the function hoist does exactly what you're describing in a slightly more generic way. Example usage:
import Control.Monad.State
import Data.Functor.Identity
import Control.Monad.Morph
foo :: State Int Integer
foo = put 1 >> return 1
bar :: StateT Int IO Integer
bar = hoist (return . runIdentity) foo
hoist is part of the MFunctor class, which is defined like this:
class MFunctor t where
hoist :: Monad m => (forall a. m a -> n a) -> t m b -> t n b
There are instances for most monad tranformers, but not ContT.