Talk:Haskell/Monad transformers
From Wikibooks, the open-content textbooks collection
Contents |
[edit] Kowey's notes
I'm using this section to note my thoughts down as I struggle to understand monad transformers. Perhaps these bits and pieces can then be used to build a tutorial. Don't pay it any heed. I'll clean it up as things become clearer to me and share them with the wikibooks community.
Perhaps one useful trick to understanding this is not to think in terms of embedding one monad instead another, but of combining two monads into a third super-monad with functionality from both.
Or another way to look at is that that monad transformers are devious creatures: we talk about them as if we're running a monad inside of another, but what we're really doing is threading the so-called outer-monad through in so-called inner monad.
[edit] lift (or liftM for monads)
Ok, so you use liftM to lift non-monadic functions into a monad. Similarly, monad transformers are monads which provide a function lift to lift functions from one monad into another. So, lift isn't anything fancy; it's just the same thing as liftM for monads.
class MonadTrans t where lift :: Monad m => m a -> t m a
Wait a sec: why is it that all of a sudden, our lift function only takes one argument and not two?
- It looks to me like lift is now lifting any monadic value, not just functions. Let's see here:
liftM :: Monad m => (a -> b) -> m a -> m b lift :: (Monad m, MonadTrans t) => m a -> t m a
- I'm not sure how well these unify, but hopefully this answers your question. --Ihope127 17:10, 17 February 2006 (UTC)
-
- Whoops! Sorry, I didn't mean to leave these old notes lying around. I think I'm ok with
liftandliftMnow (see the module page). If you want to help me understand arrows, though, see Talk:Programming:Haskell arrows/Kowey :-D -- Kowey 23:27, 17 February 2006 (UTC)
- Whoops! Sorry, I didn't mean to leave these old notes lying around. I think I'm ok with
[edit] Squeee!
Nothing serious, but today I figured out how to use the State monad (hurray for me), and I wrote a silly program using a state-monad transformer! I think I'm finally getting this stuff :-)
bottle :: StateT Int IO Int bottle = do t <- get liftIO $ putStrLn $ show t ++ " bottles of beer on the wall!" put (t - 1) return (t - 1) countBottles :: StateT Int IO () countBottles = do n <- bottle if n == 0 then liftIO $ return () else countBottles bottles :: Int -> IO ((), Int) bottles = runStateT (countBottles)
Thank you, wikibooks folks!
--Tchakkazulu 20:54, 7 September 2006 (UTC)
[edit] Instance definitions
instance (Monad m) => Monad (Maybe m) where
Shouldn't this rather be:
instance Monad Maybe where
Same for List.
--gog (2007-01-18)
- Whoops! I think the correct declaration is
instance Monad (Maybe a) where
Thanks for pointing this out -- Kowey 11:49, 18 January 2007 (UTC)
-
- Maybe a has kind *. A monad must have kind * -> *. --gog (2007-01-18)
-
-
- You are so right (and you have given me a better grasp what kinds are for). I got confused glancing down at the instance for State, but of course I forgot that the s in State s refers to the thing we're passing around. Sorry about that. -- Kowey 12:35, 18 January 2007 (UTC)
-
Shouldn't be
case b_v of Nothing -> Nothing Just v -> f v
instead of
case b_v of Nothing -> Nothing Just v -> Just $ f v
? --buben.razuma 14:27, 5 April 2007 (UTC)
-
- I don't think so, because f v already is of type
Maybe a-- Kowey 19:10, 5 April 2007 (UTC)
- I don't think so, because f v already is of type
-
-
-
- Yes, it is, that's what I mean. The second snippet is what's written and the first one is what should be. --buben.razuma 19:16, 5 April 2007 (UTC)
-
-
-
-
-
-
- Eep! You're quite right... hmm... maybe worth taking a second look at that MaybeT... Sorry, I wasn't reading carefully enough. Thought you were talking about the other way around -- Kowey 05:29, 6 April 2007 (UTC)
-
-
-
[edit] StateT definition
I don't see the point of the extra return and (<-) operator in the implementation of the bind operator of StateT:
(StateT x) >>= f = StateT $ \s -> do -- get new value, state (v,s') <- x s -- apply bound function to get new state transformation fn (StateT x') <- return $ f v -- apply the state transformation fn to the new state x' s'
Couldn't we use let instead?:
(StateT x) >>= f = StateT $ \s -> do -- get new value, state (v,s') <- x s -- apply bound function to get new state transformation fn let (StateT x') = f v -- apply the state transformation fn to the new state x' s'
-- Auders (talk) 20:28, 28 June 2008 (UTC)
- Yes, the
returnis bad style here. Changed torunStateTto mimic the use ofrunState.-- apfeλmus 10:16, 29 June 2008 (UTC)
[edit] requests
- Could a motivating example be put near the beginning of this article, e.g. something that shows exactly why you would use a transformer and how you'd use it to combine actions from two different monads.
- The table of transformers in the "cousins" section should include a row for MaybeT since MaybeT is explained in detail further down. MaybeT is probably a good candidate for the motivating example above as well, since Maybe is such a simple monad. So you'd have an example of using MaybeT near the top, then go on to the implementation explanation further down. Any other transformers mentioned in the article should also be put in the table, if they're not already there.
- I'm a bit fuzzy about this but "The type constructor Reader r is an instance of the Monad class" looks a little odd to me; I thought that types (not type constructors) were instances of type classes. Am I missing something? If there's a distinction, is it just a pedantic one?
- Thanks.
-
- Yes. The chapters about monads are currently undergoing a rewrite, starting with Haskell/Understanding monads. So, this chapter will be rewritten too and get motivation, but I can't say when :-)
- Yes. Someone please do so.
- Type constructors (i.e. things of kind
* -> *) can indeed be instances of classes two. In fact, this feature was added to Haskell exactly to make the monad class possible. So, the the constructorMaybeis the monad, not the "concrete" typeMaybe a(of kind*). Haskell/Understanding monads already contains a bit of info on this.
- -- apfeλmus 11:19, 5 August 2007 (UTC)
- I'm not sure I want to be tinkering with the article text, but here's a motivating example I emailed a friend once. I think it's right. It may be of some use:
-
- Concrete example: Suppose I was writing a server. Each client thread must be of type IO () (that's because forkIO :: IO () -> IO ThreadID)).
-
- Suppose also that this server has some configuration that (in an imperative program) would be global that the clients all need to query.
-
- > data Config = Config Foo Bar Baz
-
- One way of doing this is to use currying and making all the client threads of type Config -> IO (). Not too nice because any functions they call have to be passed the Config parameter manually. The Reader monad solves this problem but we've already got one monad. We need to wrap IO in a ReaderT. The type constructor for ReaderT is ReaderT r m a, with r the shared environment to read from, m the inner monad and a the return type. Our client_func becomes:
-
- > client_func :: ReaderT Config IO ()
-
- We can then use the ask, asks and local functions as if Reader was the only Monad:
-
- (these examples are inside do blocks)
-
- > p <- asks port
-
- (Assuming some function port :: Config -> Int or similar.)
-
- To do stuff in IO (or in the general case any inner monad) the liftIO function is used to make an IO function work in this wrapped space:
-
- (given h :: Handle, the client's handle)
-
- > liftIO $ hPutStrLn h "You lose"
- > liftIO $ hFlush h
-
- IO is once again special. For other inner monads, the lift function does the same thing. Note also that IO has no transformer and must therefore always be the innermost monad.
-
- This is all well and good, but the client_func now has type ReaderT Config IO () and forkIO needs a function of type IO (). The escape function for Reader is runReader :: Reader r a -> r -> a and similarly for ReaderT the escape function is runReaderT :: ReaderT r m a -> r -> m a:
-
- (Given some c :: Config that's been assembled from config files or the like)
-
- > forkIO (runReaderT client_func c)
-
- Will do the trick.
-
- Monad transformers are like onions. They make you cry but you learn to appreciate them. Like onions, they're also made of layers. Each layer is the functionality of a new monad, you lift monadic functions to get into the inner monads and you have transformerised functions to unwrap each layer. They're also like a present in that regard: in this example we unwrapped the outer wrapping paper to get to the present: an object of type IO (), which let us make haskell do something. 129.11.251.78 (talk) 23:26, 13 December 2007 (UTC)