On page 141 of "Yet another Haskell Tutorial" (9.7 Monad Transformers) mapTreeM action (Leaf a) = do lift (putStrLn ("Leaf" ++ show a)) b <- action a return (Leaf b) mapTreeM :: (MonadTrans t, Monad (t IO), Show a) => (a -> t IO a1) -> Tree a -> t IO (Tree a1) Why does the type signature of mapTreeM look like this? And what does it mean by "The lift tell us that we're going to be executing a command in an enclosed monad. In this case the enclosed monad is IO"? Why does the author put lift here? How does the lift work? I have no idea about the explanation in the book...is there anyone can give me some hints about this? Thank you in advance! _________________________________________________________________ Messenger10年嘉年华,礼品大奖等你拿! http://10.msn.com.cn
On 17 Jun 2009, at 19:54, .shawn wrote:
Why does the type signature of mapTreeM look like this?
Why not? I can't think of any other possibility.
And what does it mean by "The lift tell us that we're going to be executing a command in an enclosed monad. In this case the enclosed monad is IO"?
Wild guess: I think they mean exactly what they said. Literally.
Why does the author put lift here?
Because he wanted to work in a more general setting than just IO monad. He needed IO, of course, because of "putStrLn", but he wanted to give the user of this function an opportunity to do something besides just IO.
How does the lift work?
"lift" is defined in the "MonadTrans" type class, so it works differently depending on what transformer you use. You can even define your own one and "lift" would work as you write it. The only thing we know for sure is that whichever transformer you use, "lift" would have the type "m x -> t m x", where "t" is a transformer in question. So, it would be OK to apply it to, say, something of type "IO a", and you'd have something of type "t IO a" as a result.
On page 141 of "Yet another Haskell Tutorial" (9.7 Monad Transformers)
mapTreeM action (Leaf a) = do lift (putStrLn ("Leaf" ++ show a)) b <- action a return (Leaf b)
mapTreeM :: (MonadTrans t, Monad (t IO), Show a) => (a -> t IO a1) -> Tree a -> t IO (Tree a1)
Why does the type signature of mapTreeM look like this? And what does it mean by "The lift tell us that we're going to be executing a command in an enclosed monad. In this case the enclosed monad is IO"? Why does the author put lift here? How does the lift work? The idea of a monad transformer is to add a second monad (the
.shawn wrote: transformer) onto a first (I find thatoften, as in this case, IO is involved). The type parameter t (standing for the monad transformer) takes two arguments: the first is the monad being added to (IO) and the second is the normal monadic return value. For example, with the StateT transformer using a state with type Int, the type signature becomes: mapTreeM :: (MonadTrans (StateT Int), Monad (StateT Int IO), Show a) => (a -> StateT Int IO a1) -> Tree a -> StateT Int IO (Tree a1) So it takes a function from a to a1 in our combined StateT Int IO monad, a Tree of a, and gives you back a Tree of a1 in the monad, by transforming each value in the tree monadically. When you write the do block, you are writing in the StateT Int IO monad (or whatever transformer you happen to be using). Thus, the compiler expects the statements of the do block to be of this type. putStrLn gives back something in the IO monad, and there is no support in the language/type-system for automatically turning an IO thing into a StateT Int IO thing. Hence where lift comes in. lift is used to make something of the inside monad (IO) become something of the transformed monad (StateT Int IO). The call of action does not need lift, because its result is already in the StateT Int IO monad. The times where lift becomes particularly important is when you end up with two state monads (or two writer monads, or whatever) in a stack. If you had the nasty construction StateT Int (StateT Int IO) as your monad, you'd need a lift to access the inner state, but you'd leave off the lift to access the outer state. Hope that helps, Neil.
On Jun 17, 2009, at 11:54 , .shawn wrote:
mapTreeM action (Leaf a) = do lift (putStrLn ("Leaf" ++ show a)) b <- action a return (Leaf b)
mapTreeM :: (MonadTrans t, Monad (t IO), Show a) => (a -> t IO a1) -
Tree a -> t IO (Tree a1)
Why does the type signature of mapTreeM look like this?
A monad transformer "wraps" one monad around another. To get at the wrapped monad, you use (lift). The context (MonadTrans t) specifies that the type (t) is a monad transformer, meaning it provides (lift). The type (t) actually looks like (StateT s), but this isn't needed to write (mapTreeM) (it will, however, be needed when you write functions to be passed as (action)). The context (Monad (t IO)), given the MonadTrans context above, then declares that the transformer (t) is wrapped around the IO monad, so Haskell knows what you are (lift)ing *to*. You use this when you want to build a monad based on IO but with extra functionality, such as (StateT IO) which wraps an IO monad in a StateT transformer so you can carry around extra information in the state. -- brandon s. allbery [solaris,freebsd,perl,pugs,haskell] allbery@kf8nh.com system administrator [openafs,heimdal,too many hats] allbery@ece.cmu.edu electrical and computer engineering, carnegie mellon university KF8NH
participants (4)
-
.shawn -
Brandon S. Allbery KF8NH -
Miguel Mitrofanov -
Neil Brown