
michael rice wrote:
That was my suspicion. So, you can't change horses (monads) in mid-stream.
A parallel question:
main = do ... -- in the IO monad
I know I can have other *do*s in main,
if foo then do . . else do . .
but must all these other *do*s also be in the same IO monad?
Yes, if you write it like that, they have to. Let us take as an example (in the IO monad): f = do x <- getLine if null x then y else z Desugaring gives: f = getLine >>= (\x -> if null x then y else z) Since getLine :: IO String and (>>=) :: Monad m => m a -> (a -> m b) -> m b, we see that m = IO a = String So the type of (\x -> if null x then y else z) in the above expression will be String -> IO b. This means that the parameter x will be of type String, and "if null x then y else z" will be of type IO b. This implies that y and z both will be of type IO b. So if you write y and z as a do-block, this will be in the IO monad. However, there is no special rule that says "in an expression all do-blocks must have the same type". E.g., the following is a valid expression: do -- in the Maybe monad. return Nothing listToMaybe $ do -- in the [] monad. return 4 What
determines what monad a *do* is "in"? The first line after the *do*?
Type inference. E.g. f = do return [] will be of type Monad m => m [a]. There is nothing special about monads in this regard, only the "do"-notation is special: it is desugared as described elsewhere in the thread and in the Report. HTH, Jochem -- Jochem Berndsen | jochem@functor.nl