[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