Haskell Is Ugly but Not Really

Most Haskell code that I see online is really ugly, but the reason is not the language. The real reason is how people indent the code and how they name the variables. Haskell uses indentation as part of the syntax, but unlike Python, Haskell doesn't require the indentation width to be consistent. This gives you some formatting freedom, but it also gives people with poor taste the same freedom.

Take this as an example (from the Haskell wikibook):

doGuessing num = do
   putStrLn "Enter your guess:"
   guess <- getLine
   if (read guess) < num
     then do putStrLn "Too low!"
             doGuessing num
     else if (read guess) > num
            then do putStrLn "Too high!"
                    doGuessing num
            else putStrLn "You Win!"

This is just 10 lines of simple code, and it's still ugly and hard to read. Why?

The indentation is just inconsistent overall. They use 3 spaces for these 2 lines:

doGuessing num = do
   putStrLn "Enter your guess:"

But they use 2 spaces for these 2 lines:

   if (read guess) < num
     then do putStrLn "Too low!"

They also inherited the stupid indentation style from SICP, which makes the doGuessing line and then do line here differ by 1 space:

     then do putStrLn "Too low!"
             doGuessing num
     else if (read guess) > num
            then do putStrLn "Too high!"

putStrLn is just an ugly name and unfortunately it's a standard library function. The name of this function should be something like printLine, which looks much nicer and is also consistent with getLine.

This code also lacks some vertical spaces and horizontal alignment.

After fixing these issues, the code becomes:

doGuessing num = do

    printLine "Enter your guess:"
    guess <- getLine

    if      (read guess) < num then do
        printLine "Too low!"
        doGuessing num
    else if (read guess) > num then do
        printLine "Too high!"
        doGuessing num
    else
        printLine "You Win!"

This only adds 3 lines (2 of which are empty lines) and it looks way better. If you want the "correct" nesting, you can write it like this, which is still better than the original:

doGuessing num = do

    printLine "Enter your guess:"
    guess <- getLine

    if (read guess) < num
        then do
            printLine "Too low!"
            doGuessing num
        else if (read guess) > num
            then do
                printLine "Too high!"
                doGuessing num
            else
                printLine "You Win!"