Why do I need to lift when using MonadLog with Pipes - haskell

I'm trying to get logging-error working with pipes. I'm nearly there—in the sense that I have something working—but I don't think it's quite right and I don't know how to fix it. The code:
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE PartialTypeSignatures #-}
module Main where
import Protolude hiding ((<>), empty, for, get)
import Control.Monad.Log
import Text.PrettyPrint.Leijen.Text
import Pipes
testApp :: (MonadIO m, MonadLog (WithSeverity Doc) m) => m ()
testApp = logInfo $ textStrict "Logging works. Yah!"
printMessage :: (MonadIO m, MonadLog (WithSeverity Doc) m) => Consumer Text m ()
printMessage = forever $ await >>= putStrLn
readInputMessage :: (MonadIO m, MonadLog (WithSeverity Doc) m) => Producer Text m ()
readInputMessage = forever action
where
action = do
liftIO $ putStr ("> " :: Text)
liftIO getLine >>= yield
lift $ logInfo $ text "Waits with abated breath"
runMyLogging :: MonadIO m => LoggingT (WithSeverity Doc) m a -> m a
runMyLogging f = runLoggingT f (print . renderWithSeverity identity)
runPipesApp :: IO ()
runPipesApp = runMyLogging $ runEffect $
readInputMessage
>-> printMessage
runTestApp :: IO ()
runTestApp = runMyLogging testApp
main :: IO ()
main = do
runTestApp
runPipesApp
In readInputMessage I need to lift logInfo otherwise it won't compile. However testApp logInfo dosen't need to be lift'ed. Why do I need to lift in one but not the other?
Without lift this is the compilation error:
/home/rgh/dev/haskell/fa-logging/app/Main.hs:29:7: error:
• Could not deduce (MonadLog
(WithSeverity Doc) (Pipes.Proxy X () () Text m))
arising from a use of ‘logInfo’
from the context: (MonadIO m, MonadLog (WithSeverity Doc) m)
bound by the type signature for:
readInputMessage :: forall (m :: * -> *).
(MonadIO m, MonadLog (WithSeverity Doc) m) =>
Producer Text m ()
at app/Main.hs:23:1-84
• In a stmt of a 'do' block:
logInfo $ text "Waits with abated breath"
In the expression:
do liftIO $ putStr ("> " :: Text)
liftIO getLine >>= yield
logInfo $ text "Waits with abated breath"
In an equation for ‘action’:
action
= do liftIO $ putStr ("> " :: Text)
liftIO getLine >>= yield
logInfo $ text "Waits with abated breath"
|
29 | logInfo $ text "Waits with abated breath"
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-- While building package fa-logging-0.0.0 using:
/srv/cache/rgh/.stack/setup-exe-cache/x86_64-linux-nopie/Cabal-simple_mPHDZzAJ_2.0.0.2_ghc-8.2.1 --builddir=.stack-work/dist/x86_64-linux-nopie/Cabal-2.0.0.2 build lib:fa-logging exe:fa-logging --ghc-options " -ddump-hi -ddump-to-file"
Process exited with code: ExitFailure 1
I think it's not compiling because the compiler can't work out what type m is but I don't know how to fix it.

The problem is that the types in pipes aren't instances of MonadLog. In testApp, you've declared
(MonadLog (WithSeverity Doc) m) => m ()
So we are in an instance of MonadLog. Comparatively, for readInputMessage, you've declared
(MonadLog (WithSeverity Doc) m) => Producer Text m ()
So the type m is an instance of MonadLog, but we're not in type m (). We're in type Producer Text m (). Using lift then takes us into the m monad, which is what you've observed.
The solution is to make the Pipes types members of MonadLog when it's inner monad is. If you're willing to put up with orphan instances, you can write something similar to the code below.
instance (MonadLog m) => MonadLog Proxy a' a b' b m r where
askLogger = lift askLogger
localLogger f = lift . localLogger x
This should allow you to use MonadLog in any of the Pipes types as long as the inner monad is also a MonadLog.

Related

Combining StateT and ExceptT monad tranformers

I have the following little working program:
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}
module Main where
import Control.Monad.State.Strict
import Control.Monad.Except
import qualified Data.Text as T
import qualified Data.Text.Read as T
import qualified Data.Text.IO as T
main :: IO ()
main = do
_ <- execStateT loop 0
return ()
loop :: StateT Int IO ()
loop = do
liftIO $ putStrLn "Enter a Number"
line <- liftIO $ T.getLine
let ts = T.words line
checkFoo ts
loop
checkFoo :: (MonadState Int m, MonadIO m) => [T.Text] -> m()
checkFoo strs = liftIO (runExceptT
(check1 strs >>= checkNum >>= doFoo)) >>= result
where
doFoo n = liftIO $ putStrLn $ "Your number: " <> show n
check1 :: [a] -> ExceptT T.Text IO a
check1 ts =
if length ts == 1
then return $ head ts
else throwError "1 number please"
checkNum :: T.Text -> ExceptT T.Text IO Int
checkNum t = case T.decimal t of
Left _ -> throwError "input isn't a number"
Right (d, _) -> return $ d
result :: (MonadState Int m, MonadIO m) => Either T.Text () -> m ()
result (Left e) = liftIO $ T.putStrLn e
result (Right _) = return ()
Now I would like to access the value of the State Monad in the subfunction doFoo of my function checkFoo. eg:
doFoo n =
old <- lift get
let s = old + n
liftIO $ putStrLn $ "The sum of your numbers: " <> show s
lift $ put s
pure ()
I get the following error:
Main.hs:26:35: error:
• Could not deduce (MonadState a0 IO) arising from a use of ‘doFoo’
from the context: (MonadState Int m, MonadIO m)
bound by the type signature for:
checkFoo :: forall (m :: * -> *).
(MonadState Int m, MonadIO m) =>
[T.Text] -> m ()
at Main.hs:24:1-60
The type variable ‘a0’ is ambiguous
• In the second argument of ‘(>>=)’, namely ‘doFoo’
In the first argument of ‘runExceptT’, namely
‘(check1 strs >>= checkNum >>= doFoo)’
In the first argument of ‘liftIO’, namely
‘(runExceptT (check1 strs >>= checkNum >>= doFoo))’
|
26 | (check1 strs >>= checkNum >>= doFoo)) >>= result
Why is this not working? What are the necessary changes to make this working?
The problem is that when you write the expression:
check1 strs >>= checkNum >>= doFoo
this requires that each of these operations is an action is the same monad. The monad for the first two is:
ExceptT T.Text IO
which implies a type signature for doFoo:
doFoo :: Int -> ExceptT T.Text IO ()
but then you try to lift put and get operations in your revised doFoo definition. The error message is telling you that these operations aren't supported by the stateless monad ExceptT T.Text IO.
The least disruptive fix is probably to modify the type signatures for check1 and checkNum to generalize them over any MonadIO m:
check1 :: (MonadIO m) => [a] -> ExceptT T.Text m a
checkNum :: (MonadIO m) => T.Text -> ExceptT T.Text m Int
Then, checkFoo can be written as follows, without the liftIO before runExcept. I've also removed the lift before get and put. They aren't necessary, as get and put automatically lift themselves to the closest containing StateT transformer.
checkFoo :: (MonadState Int m, MonadIO m) => [T.Text] -> m ()
checkFoo strs = runExceptT (check1 strs >>= checkNum >>= doFoo) >>= result
where
-- doFoo :: (MonadState Int m, MonadIO m) => Int -> m ()
doFoo n = do
old <- get
let s = old + n
liftIO $ putStrLn $ "The sum of your numbers: " <> show s
put s
pure ()
This version runs the pipeline check1 strs >>= checkNum >>= doFoo in the monad ExceptT T.Text m where m is the same monad (MonadState Int m, MonadIO m) => m that appears in checkFoo's type signature. The result is passed to result :: (MonadIO m) => Either T.Text () -> m () again for that same monad m. In your code, checkFoo is called at m ~ StateT Int IO, which satisfies the constraints MonadState Int m and MonadIO m, so all is well with the type checker.
The full revised example:
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}
module Main where
import Control.Monad.State.Strict
import Control.Monad.Except
import qualified Data.Text as T
import qualified Data.Text.Read as T
import qualified Data.Text.IO as T
main :: IO ()
main = do
_ <- execStateT loop 0
return ()
loop :: StateT Int IO ()
loop = do
liftIO $ putStrLn "Enter a Number"
line <- liftIO $ T.getLine
let ts = T.words line
checkFoo ts
loop
checkFoo :: (MonadState Int m, MonadIO m) => [T.Text] -> m ()
checkFoo strs = runExceptT (check1 strs >>= checkNum >>= doFoo) >>= result
where
-- doFoo :: (MonadState Int m, MonadIO m) => Int -> m ()
doFoo n = do
old <- get
let s = old + n
liftIO $ putStrLn $ "The sum of your numbers: " <> show s
put s
pure ()
check1 :: (MonadIO m) => [a] -> ExceptT T.Text m a
check1 ts =
if length ts == 1
then return $ head ts
else throwError "1 number please"
checkNum :: (MonadIO m) => T.Text -> ExceptT T.Text m Int
checkNum t = case T.decimal t of
Left _ -> throwError "input isn't a number"
Right (d, _) -> return $ d
result :: (MonadState Int m, MonadIO m) => Either T.Text () -> m ()
result (Left e) = liftIO $ T.putStrLn e
result (Right _) = return ()
The problem here are the type annotations for check1 and checkNum. They don't mention the State Int monad. They should be:
check1 :: [a] -> ExceptT T.Text (StateT Int IO) a
checkNum :: T.Text -> ExceptT T.Text (StateT Int IO) Int
Then the code for checkFoo should be:
checkFoo strs = (runExceptT
(check1 strs >>= checkNum >>= doFoo)) >>= result

How to preserve the state of the monad stack in the IO exception handler?

Consider the following program.
import Control.Monad.State
import Control.Monad.Catch
ex1 :: StateT Int IO ()
ex1 = do
modify (+10)
liftIO . ioError $ userError "something went wrong"
ex2 :: StateT Int IO ()
ex2 = do
x <- get
liftIO $ print x
ex3 :: StateT Int IO ()
ex3 = ex1 `onException` ex2
main :: IO ()
main = evalStateT ex3 0
When we run the program we get the following output.
$ runhaskell Test.hs
0
Test.hs: user error (something went wrong)
However, I expected the output to be as follows.
$ runhaskell Test.hs
10
Test.hs: user error (something went wrong)
How do I preserve the intermediate state in ex1 in the exception handler ex2?
Use an IORef (or MVar or TVar or whatever) instead.
newtype IOStateT s m a = IOStateT { unIOStateT :: ReaderT (IORef s) m a }
deriving (Functor, Applicative, Monad, MonadTrans, MonadIO)
-- N.B. not MonadReader! you want that instance to pass through,
-- unlike ReaderT's instance, so you have to write the instance
-- by hand
runIOStateT :: IOStateT s m a -> IORef s -> m a
runIOStateT = runReaderT . unIOStateT -- or runIOStateT = coerce if you're feeling cheeky
instance MonadIO m => MonadState s (IOStateT s m) where
state f = IOStateT $ do
ref <- ask
liftIO $ do
s <- readIORef ref
let (a, s') = f s
writeIORef ref s'
pure a
This feels like a pattern I've seen enough times that there ought to be a Hackage package for it, but I don't know of one.

Coroutine with StateT and ST and IO

having some trouble with a group of monads I'm trying to combine.
I'm using monad-coroutine, State and lens (as I have deeply nested state).
I had an initial approach where there was a working solution. The main point here is that I can request to execute IO tasks outside of Coroutine.
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE UndecidableInstances #-}
module Main where
import Control.Monad.Coroutine (Coroutine(..), suspend, resume)
import Control.Monad.State (State, MonadState, MonadIO)
import Control.Monad.State (lift, get, put, liftIO, runState)
import System.Environment (getArgs)
type MyType = Coroutine IORequest (State MyState)
instance (MonadState s m) => MonadState s (Coroutine IORequest m) where
get = lift get
put = lift . put
io :: MonadIO m => IO a -> m a
io = liftIO
data IORequest x = forall a. RunIO (IO a) (a -> x)
instance Functor IORequest where
fmap f (RunIO x g) = RunIO x (f . g)
data MyState = MyState { _someInt :: Int }
initialState :: MyState
initialState = MyState 1
request :: Monad m => IO a -> Coroutine IORequest m a
request x = suspend (RunIO x return)
myLogic :: MyType [String]
myLogic = do
args <- request (io getArgs)
request (io (print args))
-- do a lot of useful stuff here
return args
runMyType :: MyType [String] -> MyState -> IO ()
runMyType logic state = do
let (req, state') = runState (resume logic) state
case req of
Left (RunIO cmd q') -> do
result <- cmd
runMyType (q' result) state'
Right _ -> return ()
main :: IO ()
main = runMyType myLogic initialState
Now, at some point a simple State became not enough and I am in a need of ST. I started to try to get the ST inside StateT but for some reason cannot come up with an idea how to properly handle IO outside of coroutine. Is there any way to come up with similar runMyType when there is a change in the Coroutine?
type MyType s = Coroutine IORequest (StateT (MyState s) (ST s))
initialState :: ST s (MyState s)
initialState = do
a <- newSTRef 0
return (MyState a)
Whatever I try to come up with throws some error about s escaping or Couldn't match type ‘s’ with ‘s2’ and so on... Maybe some other order of monad stacking will help? Or is it at all possible?
And another question if you have some time: what is the difference between the above MyType s and this one:
type MyType = forall s. Coroutine IORequest (StateT (MyState s) (ST s))

VTY-UI needs IO. Can I make this happen?

I'm trying to build a UI with the VTY-UI library.
I'm also using a custom monad (a few monads stacked on top of eachother).
For regular IO functions, this is not a problem. I can just lift them into my monad. However, the VTY-UI function onActivate has this type signature:
onActivate :: Widget Edit -> (Widget Edit -> IO ()) -> IO ()
Is there a way to turn a Widget Edit -> MyMonad () function into a (Widget Edit -> IO ()) without having to wrap and unwrap my monad?
I'd rather not rewrite all the library's type signatures to be MonadIO m => m () instead of IO ().
The function liftBaseOpDiscard from monad-control seems to do the trick:
import Control.Monad.Trans.Control
type MyMonad a = ReaderT Int (StateT Int IO) a
onActivate' :: Widget Edit -> (Widget Edit -> MyMonad ()) -> MyMonad ()
onActivate' = liftBaseOpDiscard . onActivate
This function has a MonadBaseControl constraint, but ReaderT and StateT on top IO already have instances for that typeclass.
As the documentation for liftBaseOpDiscard mentions, changes to the state inside the callback will be discarded.
MonadBaseControl lets you temporarily hide the upper layers of a monad stack into a value of the base monad of the stack (liftBaseWith) and afterwards pop them again, if needed (restoreM).
Edit: If we need to preserve effects that take place inside the callback (like changes in the state) one solution is to "mimic" state by using an IORef as the environment of a ReaderT. Values written into the IORef are not discarded. The monad-unlift package is built around this idea. An example:
import Control.Monad.Trans.Unlift
import Control.Monad.Trans.RWS.Ref
import Data.IORef
-- use IORefs for the environment and the state
type MyMonad a = RWSRefT IORef IORef Int () Int IO a
onActivate' :: Widget Edit -> (Widget Edit -> MyMonad ()) -> MyMonad ()
onActivate' we f = do
-- the run function will unlift into IO
UnliftBase run <- askUnliftBase
-- There's no need to manually "restore" the stack using
-- restoreM, because the changes go through the IORefs
liftBase $ onActivate we (run . f)
The monad can be run afterwards using runRWSIORefT.
For the state part: you can use this module. Thanks to whoever realized that making get and put polymorphic was a good idea.
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE UndecidableInstances #-}
module IState where
import Control.Monad
import Control.Monad.State
import Control.Monad.Reader
import Control.Monad.Trans.Class
import Control.Applicative
import Data.IORef
newtype IState s m a = IState (ReaderT (IORef s) m a)
runIState (IState a) s = do
sr <- liftIO $ newIORef s
runReaderT a sr
runIStateRef (IState a) r = runReaderT a r
instance (Monad m) => Monad (IState s m) where
return = IState . return
(IState a) >>= f = let
f' i = let (IState i') = f i in i'
in IState (a >>= f')
instance (Monad m,Functor m) => Applicative (IState s m) where
pure = return
(<*>) = ap
instance (Functor m) => Functor (IState s m) where
fmap f (IState a) = IState (fmap f a)
instance (MonadIO m) => MonadIO (IState s m) where
liftIO = lift . liftIO
instance (MonadState s' m) => MonadState s' (IState s m) where
get = lift get
put = lift . put
-- Because of this instance IState is almost a drop-in replacement for StateT
instance (MonadIO m) => MonadState s (IState s m) where
get = IState $ do
r <- ask
liftIO $ readIORef r
put v = IState $ do
r <- ask
liftIO $ writeIORef r v
instance MonadTrans (IState s) where
lift a = IState (lift a)
I managed to implement the suggestion mentioned in the comments of the question.
I give vty callbacks in IO that sends events down a Chan. Then i have another thread listening for those events and executing the appropriate actions in my own monad.

MonadException instance is not deduced

Maybe I'm doing something stupid here, but I'm getting:
No instance for (MonadException Ti)
arising from a use of `getInputLine'
in the code sample:
module Foo where
import System.Console.Haskeline
import System.Console.Haskeline.MonadException
import Control.Monad.State
import Control.Monad.IO.Class
type Ti = StateT Int IO
action :: String -> Ti ()
action s = do
n <- get
lift $ putStrLn $ show n ++ ": " ++ s
repl :: InputT Ti ()
repl = do
minput <- getInputLine "?"
case minput of
Nothing -> return ()
Just input -> lift (action input) >> repl
Now, System.Console.Haskeline.MonadException defines
MonadException IO
(MonadIO (StateT s m), MonadException m) => MonadException (StateT s m)
and Control.Monad.IO.Class:
MonadIO IO
MonadIO m => MonadIO (StateT s m)
So, shouldn't it deduce the instance for Ti automatically?
There is no instance for the Lazy state transformer, just the strict one. Use import Control.Monad.State.Strict.

Resources