I'm testing a REST server. I hit it in the IO monad and simulate it in State Db where Db tracks the supposed state of the server. The following function is supposed to run both versions and compare the results...
check :: (Eq a, MonadState d s) => s a -> IO a -> s (IO Bool)
-- or: check :: (Eq a, MonadState d s, MonadIO i) => s a -> i a -> s (i Bool)
check _ _ = (return.return) False -- for now
but when I try it with these simplest possible functions ...
simReset :: State Db ()
realReset :: IO ()
reset :: StateT Db IO Bool
reset = check simReset realReset
I get this error:
Couldn't match expected type `Bool' with actual type `IO Bool'
Expected type: StateT Db IO Bool
Actual type: StateT Db IO (IO Bool)
In the return type of a call of `check'
In the expression: check simReset realReset
Why? And how do I fix it?
(This topic started here: Lift to fix the *inside* of a monad transformer stack)
In your implementation, check is going to return an IO Bool regardless of what the state monad s is. So, when you pass simReset to check, which is a monadic action in the State Db monad, the return value is going to be State Db (IO Bool).
How to fix it depends on what you're trying to do. Based on your implementation of reset it seems like you're trying to interface with a transformer stack of the form StateT Db IO a. In this case, you're describing a kind of program in the context of StateT Db IO. There are two ways to go about this:
You can upgrade simReset to have type MonadState Db s => s () (This shouldn't actually require any implementation change).
You can define an auxiliary function that "hoists" it into the proper monad
For example:
hoistState :: Monad m => State s a -> StateT s m a
hoistState prg = StateT $ \st -> return $ runState prg st
In either case, you'll probably want to keep the action that you're performing and the result in the same monad:
check :: (Eq a, MonadIO s, MonadState d s) => s a -> IO a -> s Bool
Then, with solution one, you have
reset = check simReset realReset
And with solution two you have:
reset = check (hoistState simReset) realReset
Related
There seems to be some undocumented knowledge about the difference between Monad IO and IO. Remarks here and here) hint that IO a can be used in negative position but may have unintended consequences:
Citing Snoyman 1:
However, we know that some control flows (such as exception handling)
are not being used, since they are not compatible with MonadIO.
(Reason: MonadIO requires that the IO be in positive, not negative,
position.) This lets us know, for example, that foo is safe to use in
a continuation-based monad like ContT or Conduit.
And Kmett 2:
I tend to export functions with a MonadIO constraint... whenever it
doesn't have to take an IO-like action in negative position (as an
argument).
When my code does have to take another monadic action as an argument,
then I usually have to stop and think about it.
Is there danger in such functions that programmers should know about?
Does it for example mean that running arbitrary continuation-based action may redefine control flow giving unexpected results in ways that Monad IO based interface are safe from?
Is there danger in such functions that programmers should know about?
There is not danger. Quite the opposite, the point Snoyman and Kmett are making is that Monad IO doesn't let you lift through things with IO in a negative positive.
Suppose you want to generalize putStrLn :: String -> IO (). You can, because the IO is in a positive position:
putStrLn' :: MonadIO m => String -> m ()
putStrLn' str = liftIO (putStrLn str)
Now, suppose you want to generalize handle :: Exception e => (e -> IO a) -> IO a -> IO a. You can't (at least not with just MonadIO):
handle' :: (MonadIO m, Exception e) => (e -> m a) -> m a -> m a
handle' handler act = liftIO (handle (handler . unliftIO) (unliftIO act))
unliftIO :: MonadIO m => m a -> IO a
unliftIO = error "MonadIO isn't powerful enough to make this implementable!"
You need something more. If you're curious about how you'd do that, take a look at the implementation of functions in lifted-base. For instance: handle :: (MonadBaseControl IO m, Exception e) => (e -> m a) -> m a -> m a.
In regards to the streaming libarary What is m in Stream (Of a) m r? How could I figure this out from the documentation (sorry, a noob here)?
I'd like to understand what the type means so that I can work out my specific problem where I'm creating a stream of requests using servant, and while trying to consume it like this:
post :: Maybe Int -> ClientM [BlogPost]
post = ...
stream :: Stream (Of (ClientM [BlogPost])) ClientM ()
stream = S.map posts $ S.each $ [Just p | p <- [1..5]]
main = do
let url = ...
S.print $ S.map (\x -> runClientM x url) stream
But I'm getting the following error:
• Couldn't match type ‘ClientM’ with ‘IO’
Expected type: S.Stream (S.Of (ClientM [BlogPost])) IO ()
Actual type: S.Stream (S.Of (ClientM [BlogPost])) ClientM ()
If given in isolation, the m in Stream (Of a) m r could be any type.
When considering specific functions in the module, be aware of the type constraint. For example, the yield function has this type:
yield :: Monad m => a -> Stream (Of a) m ()
Here, m is constrained to be any type that's a Monad instance. This could be IO, [] (list), Maybe, State, Reader, etc.
Another function has this type:
stdinLn :: MonadIO m => Stream (Of String) m ()
Here, m is constrained to be any type that's a MonadIO instance. The MonadIO type class is a subclass of Monad, in the sense that for a type to be a MonadIO, it must already be a Monad.
AFAICT, IO is also a MonadIO, but e.g. Maybe isn't.
Thus, some of the functions in the module are more constrained than others. The yield function is less constrained than the stdinLn function. You can use Maybe as m with yield, but not with stdinLn.
Regarding your specific problem, there's not enough information in the OP for a repro, but it looks like main uses the map function from Streaming.Prelude:
map :: Monad m => (a -> b) -> Stream (Of a) m r -> Stream (Of b) m r
Here, m must be a Monad instance. In Haskell, the main function must have the type IO (), so when using do notation, the Monad instance is inferred to be IO. The error message states that the compiler expects m to be IO.
Consider the State type - or at least a simplified version:
newtype State s a = State { runState :: s -> (a, s) }
Now, let's say we want to derive the StateT monad transformer. transformers defines it as follows:
newtype StateT s m a = StateT { runStateT :: s -> m (a, s) }
Here, the m has been placed on the right of the function arrow, but outside the tuple. However, if we didn't know the correct answer, we might instead put m somewhere else:
newtype StateT s m a = StateT { runStateT :: m (s -> ( a, s)) }
newtype StateT s m a = StateT { runStateT :: s -> (m a, s) }
Obviously the version in transformers is correct, but why? More generally, how does one know where to put the type variable for the 'inner' monad when defining a monad transformer? Generalising even more, is there a similar rule for comonad transformers?
I think the difference can be easily understood when m ~ IO:
s -> IO (a, s)
is the type of an action which can read the current state s, perform IO depending on that (e.g. printing the current state, reading a line from the user), and then produce both the new state s, and a return value a.
Instead:
IO (s -> (a, s))
is the type of an action which immediately performs IO, without knowing the current state. After all the IO is over, it returns a pure function mapping the old state into a new state and a return value.
This is similar to the previous type, since the new state and return value can depend both on the previous state and the IO. However, the IO can not depend on the current state: e.g., printing the current state is disallowed.
Instead,
s -> (IO a, s)
is the type of an action which reads the current state s, and then performs IO depending on that (e.g. printing the current state, reading a line from the user), and then produce a return value a. Depdnding on the current state, bot not on the IO, a new state is produced. This type is effectively isomorphic to a pair of functions (s -> IO a, s -> s).
Here, the IO can read a line from the user, and produce a return value a depending on that, but the new state can not depend on that line.
Since the first variant is more general, we want that as our state transformer.
I don't think there's a "general rule" for deciding where to put m: it depends on what we want to achieve.
This program compiles without problems:
bar :: MonadIO m
=> m String
bar = undefined
run2IO :: MonadIO m
=> m String
-> m String
run2IO foo = liftIO bar
When I change bar to foo (argument name),
run2IO :: MonadIO m
=> m String
-> m String
run2IO foo = liftIO foo
I get:
Couldn't match type ‘m’ with ‘IO’
‘m’ is a rigid type variable bound by
the type signature for run2IO :: MonadIO m => m String -> m String
...
Expected type: IO String
Actual type: m String ...
Why are the 2 cases are not equivalent?
Remember the type of liftIO:
liftIO :: MonadIO m => IO a -> m a
Importantly, the first argument must be a concrete IO value. That means when you have an expression liftIO x, then x must be of type IO a.
When a Haskell function is universally quantified (using an implicit or explicit forall), then that means the function caller chooses what the type variable is replaced by. As an example, consider the id function: it has type a -> a, but when you evaluate the expression id True, then id takes the type Bool -> Bool because a is instantiated as the Bool type.
Now, consider your first example again:
run2IO :: MonadIO m => m Integer -> m Integer
run2IO foo = liftIO bar
The foo argument is completely irrelevant here, so all that actually matters is the liftIO bar expression. Since liftIO requires its first argument to be of type IO a, then bar must be of type IO a. However, bar is polymorphic: it actually has type MonadIO m => m Integer.
Fortunately, IO has a MonadIO instance, so the bar value is instantiated using IO to become IO Integer, which is okay, because bar is universally quantified, so its instantiation is chosen by its use.
Now, consider the other situation, in which liftIO foo is used, instead. This seems like it’s the same, but it actually isn’t at all: this time, the MonadIO m => m Integer value is an argument to the function, not a separate value. The quantification is over the entire function, not the individual value. To understand this more intuitively, it might be helpful to consider id again, but this time, consider its definition:
id :: a -> a
id x = x
In this case, x cannot be instantiated to be Bool within its definition, since that would mean id could only work on Bool values, which is obviously wrong. Effectively, within the implementation of id, x must be used completely generically—it cannot be instantiated to a specific type because that would violate the parametricity guarantees.
Therefore, in your run2IO function, foo must be used completely generically as an arbitrary MonadIO value, not a specific MonadIO instance. The liftIO call attempts to use the specific IO instance, which is disallowed, since the caller might not provide an IO value.
It is possible, of course, that you might want the argument to the function to be quantified in the same way as bar is; that is, you might want its instantiation to be chosen by the implementation, not the caller. In that case, you can use the RankNTypes language extension to specify a different type using an explicit forall:
{-# LANGUAGE RankNTypes #-}
run3IO :: MonadIO m => (forall m1. MonadIO m1 => m1 Integer) -> m Integer
run3IO foo = liftIO foo
This will typecheck, but it’s not a very useful function.
In the first, you're using liftIO on bar. That actually requires bar :: IO String. Now, IO happens to be (trivially) an instance on MonadIO, so this works – the compiler simply throws away the polymorphism of bar.
In the second case, the compiler doesn't get to decide what particular monad to use as the type of foo: it's fixed by the environment, i.e. the caller can decide what MonadIO instance it should be. To again get the freedom to choose IO as the monad, you'd need the following signature:
{-# LANGUAGE Rank2Types, UnicodeSyntax #-}
run2IO' :: MonadIO m
=> (∀ m' . MonadIO m' => m' String)
-> m String
run2IO' foo = liftIO foo
... however I don't think you really want that: you might then as well write
run2IO' :: MonadIO m => IO String -> m String
run2IO' foo = liftIO foo
or simply run2IO = liftIO.
I'm still learning Haskell and need help with the type inference please!
Using packages SDL and Yampa
I get the following type signature from FRP.Yampa.reactimate:
(Bool -> IO (DTime, Maybe a))
and I want to use it for:
myInput :: Bool -> IO (DTime, Maybe [SDL.Event])
myInput isBlocking = do
event <- SDL.pollEvent
return (1, Just [event])
...
reactimate myInit myInput myOutput mySF
but it says
Couldn't match expected type `()'
against inferred type `[SDL.Event]'
Expected type: IO (DTime, Maybe ())
Inferred type: IO (DTime, Maybe [SDL.Event])
In the second argument of `reactimate', namely `input'
In the expression: reactimate initialize input output process
I thought Maybe a allows me to use anything, even a SDL.Event list?
Why is it expecting Maybe () when the type signature is actually Maybe a?
Why does it want an empty tuple, or a function taking no arguments, or what is () supposed to be?
The full type signature of reactimate is
IO a -- # myInit
-> (Bool -> IO (DTime, Maybe a)) -- # myInput
-> (Bool -> b -> IO Bool) -- # myOutput
-> SF a b -- # mySF
-> IO ()
The same a and b must match, that means if your myInput has type Bool -> IO (DTime, Maybe [SDL.Event]), then all other a must also be [SDL.Event]. Hence, to match the types, you need to ensure
myInit :: IO [SDL.Event] -- # **not** IO ().
mySF :: SF [SDL.Event] b
BTW, () is the unit type.