Good Haskell coding style of if/else control block?

Question!

I'm learning Haskell hope it could let me getting closer to functional programming, before learing it, I mostly use C-sytanx like languages, like C, Java or D Programming Language.

I followed the tutorial on Wikibook, it goes well so far, I could understand most of them before the chapter "Simple input and output"

But I do have a little question about the coding style of if/else control block used by the tutorial.

In the wikibook, the code look like the following:

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 do putStrLn "You Win!"

It makes me confusing, because this coding style is totally volate "Good Coding Style" in C-sytnax like programming language, where we should ident if/else if/else at same column.

I know it just not work in Haskell, because it would cause parse error if I ident "else" at same column of "if".

But what about the following one? I think it is much more clear then the above one. But since the above is used by Wikibook and Yet Another Haskell Tutorial, which marked "best tutorial available online" at offical Haskell website, so I'm not sure whether this coding style is a convention in Haskell programs.

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 do 
            putStrLn "You Win!"

So, I'm curious about which coding style is used more often or is there anthoer coding style for this piece of code? I would like to know it too.

By : Brian Hsu


Answers

Haskell style is functional, not imperative! Rather than "do this then that," think about combining functions and describing what your program will do, not how.

In the game, your program asks the user for a guess. A correct guess is a winner. Otherwise, the user tries again. The game continues until the user guesses correctly, so we write that:

main = untilM (isCorrect 42) (read `liftM` getLine)

This uses a combinator that repeatedly runs an action (getLine pulls a line of input and read converts that string to an integer in this case) and checks its result:

untilM :: Monad m => (a -> m Bool) -> m a -> m ()
untilM p a = do
  x <- a
  done <- p x
  if done
    then return ()
    else untilM p a

The predicate (partially applied in main) checks the guess against the correct value and responds accordingly:

isCorrect :: Int -> Int -> IO Bool
isCorrect num guess =
  case compare num guess of
    EQ -> putStrLn "You Win!"  >> return True
    LT -> putStrLn "Too high!" >> return False
    GT -> putStrLn "Too low!"  >> return False

The action to be run until the player guesses correctly is

read `liftM` getLine

Why not keep it simple and just compose the two functions?

*Main> :type read . getLine

<interactive>:1:7:
    Couldn't match expected type `a -> String'
           against inferred type `IO String'
    In the second argument of `(.)', namely `getLine'
    In the expression: read . getLine

The type of getLine is IO String, but read wants a pure String.

The function liftM from Control.Monad takes a pure function and “lifts” it into a monad. The type of the expression tells us a great deal about what it does:

*Main> :type read `liftM` getLine
read `liftM` getLine :: (Read a) => IO a

It's an I/O action that when run gives us back a value converted with read, an Int in our case. Recall that readLine is an I/O action that yields String values, so you can think of liftM as allowing us to apply read “inside” the IO monad.

Sample game:

1
Too low!
100
Too high!
42
You Win!


The way Haskell interprets if ... then ... else within a do block is very much in keeping with the whole of Haskell's syntax.

But many people prefer a slightly different syntax, permitting then and else to appear at the same indentation level as the corresponding if. Therefore, GHC comes with an opt-in language extension called DoAndIfThenElse, which permits this syntax.

The DoAndIfThenElse extension is made into part of the core language in the latest revision of the Haskell specification, Haskell 2010.

By : yfeldblum


This video can help you solving your question :)
By: admin