From source:
module Control.Monad.IO.Class (
MonadIO(..)
) where
-- | Monads in which 'IO' computations may be embedded.
-- Any monad built by applying a sequence of monad transformers to the
-- 'IO' monad will be an instance of this class.
--
-- Instances should satisfy the following laws, which state that 'liftIO'
-- is a transformer of monads:
--
-- * #'liftIO' . 'return' = 'return'#
--
-- * #'liftIO' (m >>= f) = 'liftIO' m >>= ('liftIO' . f)#
class (Monad m) => MonadIO m where
-- | Lift a computation from the 'IO' monad.
liftIO :: IO a -> m a
instance MonadIO IO where
liftIO = id
What is meant by
liftIO (m >>= f) = liftIO m >>= (liftIO . f)
And in particular, what is meant by (m >>= f)? Here m is a function on types and f a function on values. So isn't that notation non-sense?
As noted in the comments, the m in the law is just a value variable, and is not the same m as the type variable m used in the class definition.
If you rewrite the law as:
liftIO (act >>= f) = liftIO act >>= (liftIO . f)
equivalent to:
liftIO $ do x <- act === do x <- liftIO act
f x liftIO (f x)
with the understanding that act is an IO action, then it may be clearer what's going on. The expression act >>= f represents the composite IO action that, when run, will run the IO action act and pass its return value to f to generate a new IO action (say act2) that will then be run.
The law just says that lifting this composite IO action into a different monad creates an action that, when run, is equivalent running a lifted version of act, passing its return value to f to generate a new IO action, lifting that action to the other monad, and running that.
As a concrete example, getLine >>= print is the IO action that reads a line and then prints its value in Haskell string syntax. The law says that you can either lift this directly into another monad:
liftIO $ do x <- getLine
print x
to get an action that does that, or you can lift the IO action parts separately:
do x <- liftIO getLine
liftIO (print x)
and get exactly the same action.
Related
I try to understand the lifting principle by example, and found this:
https://github.com/graninas/Functional-Design-and-Architecture/blob/1736abc16d3e4917fc466010dcc182746af2fd0e/First-Edition/BookSamples/CH03/MonadStack.hs
Then, if I change it's lift (lift (putStrLn "bla-bla")) to putStrLn "bla-bla", the compiler throw error!
I do this base on my understanding: do block are just syntax sugar, and each line's result are pass into next line. If the next line do not use the args that passed from previous line, the type of args won't cause type confliction, I think.
Take bellow as example, while x<- getLine can past compiling
test:: IO ()
test = do
x <- getLine -- discarded and compiler don't care it type
let a = "bla-bla" -- discarded and compiler don't care it type
putStrLn $ "You type are discarded: "
Now back to the calculations function:
type Data = Map.Map Int String
type StateIO = StateT Data IO
type MaybeStateIO a = MaybeT StateIO a
calculations :: MaybeStateIO ()
calculations = do
lift (lift (putStrLn "bla-bla")) -- if I change this to `putStrLn "bla-bla"`, it failed compiling.
lift (modify (Map.insert 3 "3"))
lift (modify (Map.insert 1 "1"))
mb <- lift (get >>= (return . Map.lookup 1))
lift (lift (print mb))
main = runStateT (runMaybeT calculations) Map.empty
I don't understand is that to compiler require lifting on putStrLn "bla-bla".
Isn't it enough when return value of the last line of do block match the function's return value?
In this example, how does the compiler decide the do block's value type? according to function's signature?
Can anyone explains the lift for me? How do it work, when to use, etc.
Isn't it enough when return value of the last line of do block match the function's return value?
No, since that would mean that you could write a do block where the first item for example would use the instance of [] for Monad whereas the next would use for example Maybe or IO, but then how would x <- some_list make sense for a list of putStrLn x? All lines in the do block should be of type m a with m the same instance of Monad, and the as can have different types for each line. If you write a do block with:
foo = do
x <- exp1
exp2
then this is translated to exp1 >>= \x -> exp2, and since (>>=) :: Monad m => m a -> (a -> m b) -> m b operates where the two operands share the same monad m, this thus means that exp1 :: m a and exp2 :: m b thus need to work with the same monadic type m.
You require to perform lifting twice since the line should have as type MaybeT (StateT Data IO) a whereas putStrLn "bla-bla" has IO a, it thus requires one lift :: (MonadTrans t, Monad m) => m a -> t m a to lift it to StateT Data IO a and another to finally lift it to a MaybeT (StateT Data IO) a.
The value of calculations is a MaybeStateIO value. That's the monad you are operating in, so that's what every line of the do block has to produce. But putStrLn "bla-bla" does not produce a MaybeStateIO value; it just produces an IO value. The first lift takes that IO value and returns a StateIO value; the second lift takes that StateIO value and returns a MaybeStateIO value.
Remember,
do
a
b
is just syntactic sugar for a >> b, and (>>) :: Monad m => m a -> m b -> m b needs values from the same monad as arguments. It's only the "return value" (a and b) of the monad that can vary from line to line; the monad m itself is fixed.
I've looked at the some instances of MonadTrans, for MaybeT the implementation looks like this:
instance MonadTrans MaybeT where
lift = MaybeT . liftM Just
As I understand the instance for MonadIO is used to do a variable number of lifts from the inner most, a IO monad, directly to the outermost. For the MaybeT case it looks like this:
instance (MonadIO m) => MonadIO (MaybeT m) where
liftIO = lift . liftIO
What I don't understand is how this recursive function escapes the infinite loop. What is the base case?
Perhaps surprisingly, the definition below is not recursive, even if it looks such.
instance (MonadIO m) => MonadIO (MaybeT m) where
liftIO = lift . liftIO
This is because the liftIO on the left hand side is the liftIO for the MaybeT m monad, while the liftIO on the right hand side is the liftIO for the m monad.
Hence, this simply defines liftIO in one monad in terms of the liftIO for another monad. No recursion here.
This is similar to e.g.
instance (Show a, Show b) => Show (a,b) where
show (x,y) = "(" ++ show x ++ ", " ++ show y ++ ")"
Above, we define how to print a pair depending on how to print their components. It looks recursive, but it is not really such.
It could help visualizing this by inserting explicit type arguments, at least mentally:
-- pseudo-code
instance (Show a, Show b) => Show (a,b) where
show #(a,b) (x,y) =
"(" ++ show #a x ++ ", " ++ show #b y ++ ")"
Now show #(a,b), show #a, and show #b are distinct functions.
Simple equational reasoning and rewriting definitions for some specialization can help you. Base case for MonadIO is IO. MaybeT is monad transformer, so lets combine MaybeT and IO in some simple example.
foo :: MaybeT IO String
foo = liftIO getLine
Now let's rewrite this function definition applying instance implementations from your question step by step.
foo
= liftIO {- for MaybeT -} getLine
= lift (liftIO {- for IO here -} getLine) -- step 2
= lift (id getLine)
= lift getLine
= MaybeT (liftM Just getLine)
getLine has type IO String
liftM Just getLine has type IO (Maybe String)
MaybeT m a constructor needs value of type m (Maybe a) where m = IO and a = String in our case.
Probably hardest step to analyze is step 2. But in reality it's very easy if you remind yourself types of liftIO :: IO a -> m a and lift :: Monad m => m a -> t m a. So all work is done by type inference.
Somewhat mystified by the following code. In non-toy version of the problem I'm trying to do a monadic computation in a monad Result, the values of which can only be constructed from within IO. Seems like the magic behind IO makes such computations strict, but I can't figure out how exactly that happens.
The code:
data Result a = Result a | Failure deriving (Show)
instance Functor Result where
fmap f (Result a) = Result (f a)
fmap f Failure = Failure
instance Applicative Result where
pure = return
(<*>) = ap
instance Monad Result where
return = Result
Result a >>= f = f a
Failure >>= _ = Failure
compute :: Int -> Result Int
compute 3 = Failure
compute x = traceShow x $ Result x
compute2 :: Monad m => Int -> m (Result Int)
compute2 3 = return Failure
compute2 x = traceShow x $ return $ Result x
compute3 :: Monad m => Int -> m (Result Int)
compute3 = return . compute
main :: IO ()
main = do
let results = mapM compute [1..5]
print $ results
results2 <- mapM compute2 [1..5]
print $ sequence results2
results3 <- mapM compute3 [1..5]
print $ sequence results3
let results2' = runIdentity $ mapM compute2 [1..5]
print $ sequence results2'
The output:
1
2
Failure
1
2
4
5
Failure
1
2
Failure
1
2
Failure
Nice test cases. Here's what's happening:
in mapM compute we see laziness at work, as usual. No surprise here.
in mapM compute2 we work inside the IO monad, whose mapM definition will demand the whole list: unlike Result which skips the tail of the list as soon as Failure is found, IO will always scan the whole list. Note the code:
compute2 x = traceShow x $ return $ Result x
So, the above wil print the debug message as soon as each element of the list of IO actions is accessed. All are, so we print everything.
in mapM compute3 we now use, roughly:
compute3 x = return $ traceShow x $ Result x
Now, since return in IO is lazy, it will not trigger the traceShow when returning the IO action. So, when mapM compute3 is run, no message is seen. Instead, we see messages only when sequence results3 is run, which forces the Result -- not all of them, but only as much as needed.
the final Identity example is also quite tricky. Note this:
> newtype Id1 a = Id1 a
> data Id2 a = Id2 a
> Id1 (trace "hey!" True) `seq` 42
hey!
42
> Id2 (trace "hey!" True) `seq` 42
42
when using a newtype, at runtime there is no boxing/unboxing (AKA lifting) involved, so forcing a Id1 x value causes x to be forced. With data types this does not happen: the value is wrapped in a box (e.g. Id2 undefined is not equivalent to undefined).
In your example, you add an Identity constructor, but that is from the newtype Identity!! So, when calling
return $ traceShow x $ Result x
the return here does not wrap anything, and the traceShow is immediately triggered as soon as mapM is run.
Your Result type appears to be virtually identical to Maybe, with
Result <-> Just
Failure <-> Nothing
For the sake of my poor brain, I'll stick to Maybe terminology in the rest of this answer.
chi explained why IO (Maybe a) does not short-circuit the way you expected. But there is a type you can use for this sort of thing! It's essentially the same type, in fact, but with a different Monad instance. You can find it in Control.Monad.Trans.Maybe. It looks something like this:
newtype MaybeT m a = MaybeT
{ runMaybeT :: m (Maybe a) }
As you can see, this is just a newtype wrapper around m (Maybe a). But its Monad instance is very different:
instance Monad m => Monad (MaybeT m) where
return a = MaybeT $ return (Just a)
m >>= f = MaybeT $ do
mres <- runMaybeT m
case mres of
Nothing -> return Nothing
Just a -> runMaybeT (f a)
That is, m >>= f runs the m computation in the underlying monad, getting Maybe something or other. If it gets Nothing, it just stops, returning Nothing. If it gets something, it passes that to f and runs the result. You can also turn any m action into a "successful" MaybeT m action using lift from Control.Monad.Trans.Class:
class MonadTrans t where
lift :: Monad m => m a -> t m a
instance MonadTrans MaybeT where
lift m = MaybeT $ Just <$> m
You can also use this class, defined somewhere like Control.Monad.IO.Class, which is often clearer and can be much more convenient:
class MonadIO m where
liftIO :: IO a -> m a
instance MonadIO IO where
liftIO m = m
instance MonadIO m => MonadIO (MaybeT m) where
liftIO m = lift (liftIO m)
I just asked this on haskell-cafe, but I figure I might as well ask here too. Is the following Monad instance for Backwards m valid?
{-# Language RecursiveDo #-}
import Control.Applicative.Backwards
import Control.Monad.Fix
instance MonadFix m => Monad (Backwards m) where
m >>= f = Backwards $
do
fin <- forwards (f int)
int <- forwards m
pure fin
If so, could I also add this?
instance MonadFix m => MonadFix (Backwards m) where
mfix f = Backwards $ mfix (forwards . f)
No, it is not valid; the monad laws at best hold in some approximate fashion. As Petr Pudlák's answer shows, Backwards m >>= f does not behave very nicely when f is strict in its argument.
According to the monad laws,
pure () >>= (\() -> m) = m
But with this instance, if I'm not mistaken,
pure () >>= (\() -> m) = Backwards $ do
fin <- forwards (int `seq` m)
int <- pure ()
pure fin
= Backwards $ fmap fst $ mfix $ \ ~(_, int) -> do
fin <- forwards (int `seq` m)
pure (fin, ())
If the underlying monad is "strict" (i.e., its >>= is strict in its left operand), this will diverge.
For this f would need to be lazy, that is, the effect must not depend on the argument. The docs say
mfix f executes the action f only once, with the eventual output fed back as the input. Hence f should not be strict, for then mfix f would diverge.
Buf if f in your case m >>= f will be strict, then so will be the block passed to mfix.
Let's consider a practical example where m is readLine >>= putStrLn. Reversing the order would mean "print the data, then read it". Unless the effect of the function behind >>= doesn't depend on the input, this diverges.
Particularly, I need to be able to combine the CGI monad with the IO monad, but an example of how to combine the IO monad with the Maybe monad might be even better...
I assume you want to use the Maybe monad for early termination (like break or return in C).
In that case you should use MaybeT from the MaybeT package (cabal install MaybeT).
main = do
runMaybeT . forever $ do
liftIO $ putStrLn "I won't stop until you type pretty please"
line <- liftIO getLine
when ("pretty please" == line) mzero
return ()
MaybeT is a monad transformer version of the maybe monad.
Monad transformers "add functionality" to other monads.
You don't exactly say how you want to combine IO and Maybe, but I assume you have many functions that return IO (Maybe a) that you want to combine easily. Basically you want to treat IO (Maybe a) as a separate type with it's own Monad instance:
newtype IOMaybe a = IOM (IO (Maybe a))
-- "unpack" a value of the new type
runIOMaybe :: IOMaybe a -> IO (Maybe a)
runIOMaybe (IOM a) = a
instance Monad IOMaybe where
-- bind operator
(IOM ioa) >>= f = IOM $ do
a <- ioa
case a of
Nothing -> return Nothing
Just v -> runIOMaybe (f v)
-- return
return a = IOM (return (Just a))
-- maybe also some convenience functions
returnIO :: IO a -> IOMaybe a
returnIO ioa = IOM $ do
v <- ioa
return (Just v)
returnMaybe :: Maybe a -> IOMaybe a
returnMaybe ma = IOM (return ma)
With this you can use the do-Notation to combine functions that return IO (Maybe a), IO a or Maybe a:
f1 :: Int -> IO (Maybe Int)
f1 0 = return Nothing
f1 a = return (Just a)
main = runIOMaybe $ do
returnIO $ putStrLn "Hello"
a <- returnMaybe $ Just 2
IOM $ f1 a
return ()
Generally something that combines and modifies monads like this is called a monad transformer, and GHC comes with a package that includes monad transformers for common cases. If there is something in this monad transformer library that fits your scenario depends on how exactly you want to combine Maybe and IO.
In what sense do you want to combine the monads?
f :: Int -> IO (Maybe Int)
f x = do
putStrLn "Hello world!"
return $ if x == 0 then Nothing else Just x
Can be evaluated to:
[1 of 1] Compiling Main ( maybe-io.hs, interpreted )
Ok, modules loaded: Main.
*Main> f 0
Hello world!
Nothing
*Main> f 3
Hello world!
Just 3