(I hope this question is on-topic -- I tried searching for an answer but didn\'t find a definitive answer. If this happens to be off-topic or already answered, please mo
It's not a joke, and I believe it. I'll try to keep this accessible for those who don't know any Haskell. Haskell uses do-notation (among other things) to allow you to write imperative code (yes, it uses monads, but don't worry about that). Here's some of the advantages that Haskell gives you:
Easy creation of subroutines. Let's say that I want a function to print a value to stdout and stderr. I can write the following, defining the subroutine with one short line:
do let printBoth s = putStrLn s >> hPutStrLn stderr s
printBoth "Hello"
-- Some other code
printBoth "Goodbye"
Easy to pass code around. Given that I've written the above, if I now want to use the printBoth function to print out all of a list of strings, that's easily done by passing my subroutine to the mapM_ function:
mapM_ printBoth ["Hello", "World!"]
Another example, although not imperative, is sorting. Let's say you want to sort strings solely by length. You can write:
sortBy (\a b -> compare (length a) (length b)) ["aaaa", "b", "cc"]
Which will give you ["b", "cc", "aaaa"]. (You can write it shorter than that, too, but never mind for now.)
Easy to re-use code. That mapM_ function is used a lot, and replaces for-each loops in other languages. There's also forever which acts like a while (true), and various other functions that can be passed code and execute it in different ways. So loops in other languages are replaced by these control functions in Haskell (which are not special -- you can define them yourself very easily). In general this makes it hard to get the loop condition wrong, just like for-each loops are harder to get wrong than the long-hand iterator equivalents (e.g. in Java), or array-indexing loops (e.g. in C).
Contained side effects. Let's say that I want to read a line from stdin, and write it on stdout after applying some function to it (we'll call it foo). You can write:
do line <- getLine
putStrLn (foo line)
I know immediately that foo doesn't have any unexpected side effects (like updating a global variable, or deallocating memory, or whatever), because it's type must be String -> String, which means it is a pure function; whatever value I pass, it must return the same result every time, without side effects. Haskell nicely separates the side-effecting code from the pure code. In something like C, or even Java, this is not obvious (does that getFoo() method change state? You'd hope not, but it might do...).
There's probably a few more advantages besides, but those are the ones that come to mind.