[Haskell-beginners] How to unnest "do"

Brent Yorgey byorgey at seas.upenn.edu
Sun Jan 27 23:42:04 CET 2013


On Sun, Jan 27, 2013 at 11:29:18PM +0100, Martin Drautzburg wrote:
> On Sunday, 27. January 2013 20:43:58 Ertugrul Söylemez wrote:
> > Hi there Martin,
> > 
> > since the nested 'do' makes sense, there is little you can do about it.
> > However, you can make the code more beautiful and restructure it a bit.
> > This is how I would have written it:
> > 
> >     import Control.Applicative
> >     import System.Environment
> >     import System.IO
> > 
> >     stats :: String -> String
> >     stats =
> >         unwords .
> >         sequence [show . length . words,
> >                   show . length . lines,
> >                   show . length]
> > 
> >     main :: IO ()
> >     main = do
> >         args <- getArgs
> >         case args of
> >           [fn] -> fmap stats (readFile fn) >>= putStrLn --<----
> >           _    -> hPutStrLn stderr "Usage: wc FNAME"
> > 
> > This improves the statistics code slightly, but uses some monadic
> > machinery you may not be familiar with.  
> 
> Thanks, this looks much nicer and is very inspiring.
> 
> But let me see if I get this correctly:
> 
> readFile fn returns IO String
> 
> I cannot see the String itself, but I can map the stats function over it, 
> which gives me anoter IO String. This works, because every Monad is also a 
> functor. Another way of looking at this is that fmap lifts the String->String 
> function "stats" to (IO String) -> (IO String),
> 
> Again I cannot see the String inside the IO String, but I can pass it to a 
> function String->IO String, using (>>=) and putStrLn is such a function, which 
> also does what I need.
> 
> I cannot use the same fmap mechanism ("fmap putStrLn"), because putStrLn is 
> already String -> IO String and fmap would lift both sides of "->".
> 
> I tried to find another way of passing the IO String to putStrLn, but there 
> aren't many options. If I want to use putStrLn, then I need to get the String 
> out of the IO String. AFAICS (>>=) is the only way to do this (other than do 
> notation). In contrast to fmap,  (>>=) lifts only the left side.
> 
> For the hell of it, I tried to replace putStrLn by a String -> Maybe String 
> function. This does not work and it made me realize, that the signature of 
> (>>=) :: m a -> (a-> m b) -> m b demands that the the Monad "m" is the same 
> all the way through, and only its type parameter can change.
> 
> And the Applicative import is not really needed.
> 
> Is this about correct?

That all sounds right to me!

-Brent



More information about the Beginners mailing list