[Haskell-beginners] How to unnest "do"

Martin Drautzburg Martin.Drautzburg at web.de
Sun Jan 27 23:29:18 CET 2013


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?

When I have a program, which accesses stdin and stdout and a database, I 
suppose I will have to do things like this a lot?

Sorry for the long post, but I am getting kindof excieted. 





More information about the Beginners mailing list