Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Good Haskell coding style of if/else control block?

I'm learning Haskell in the hope that it will help me get closer to functional programming. Previously, I've mostly used languages with C-like syntax, like C, Java, and D.

I have a little question about the coding style of an if/else control block used by the tutorial on Wikibooks. The code looks 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 confused, because this coding style totally violates the recommended style in C-like languages, where we should indent if, else if, and else at the same column.

I know it just does not work in Haskell, because it would be a parse error if I indented else at the same column as if.

But what about the following style? I think it is much more clear than the above one. But since the above is used by Wikibooks and Yet Another Haskell Tutorial, which is marked "best tutorial available online" at the official Haskell website, 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 another coding style for this piece of code?

like image 946
Brian Hsu Avatar asked Sep 24 '08 13:09

Brian Hsu


People also ask

Should Haskell without IF-THEN-ELSE syntax be used?

Haskell without if-then-else syntax makes Haskell more logical and consistent. There is no longer confusion to beginners like: "What is so special about if-then-else, that it needs a separate syntax? I though it could be simply replaced by a function. Maybe there is some subtlety that I'm not able to see right now."

How to use the IF-ELSE conditional statement in Haskell?

Here is the general syntax of using the if-else conditional statement in Haskell. if<Condition> then <True-Value>else <False-Value> In the above expression, Condition − It is the binary condition which will be tested. True-Value − It refers to the output that comes when the Condition satisfies

What is this Haskell best practices document?

This document is a collection of best-practices inspired by commercial and free open source Haskell libraries and applications. The purpose of this document is to help developers and people working on Haskell code-bases to have a smoother experience while dealing with code in different situations.

What is false false value in Haskell?

False-Value − It refers to the output that comes when the condition does not satisfy. As Haskell codes are interpreted as mathematical expressions, the above statement will throw an error without else block. The following code shows how you can use the if-else statement in Haskell −


1 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!
like image 98
Greg Bacon Avatar answered Sep 24 '22 00:09

Greg Bacon