Good Haskell coding style of if/else control block?

后端 未结 8 1543
慢半拍i
慢半拍i 2021-01-31 09:15

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.

相关标签:
8条回答
  • 2021-01-31 09:27

    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!
    0 讨论(0)
  • 2021-01-31 09:27

    You can also use explicit grouping with curly braces. See the layout section of http://www.haskell.org/tutorial/patterns.html

    I wouldn't recommend that though. I've never seen anyone use explicit grouping besides in a few special cases. I usually look at the Standard Prelude code for examples of style.

    0 讨论(0)
  • 2021-01-31 09:30

    Note that the fact that you have to indent the 'then' and 'else' inside a 'do' block is considered a bug by many. It will probably be fixed in Haskell' (Haskell prime), the next version of the Haskell specification.

    0 讨论(0)
  • 2021-01-31 09:44

    I use a coding style like your example from Wikibooks. Sure, it doesn't follow the C guidelines, but Haskell's not C, and it's fairly readable, especially once you get used to it. It's also patterned after the style of algorithms used in many textbooks, like Cormen.

    0 讨论(0)
  • 2021-01-31 09:46

    You will see a bunch of different indentation styles for Haskell. Most of them are very hard to maintain without an editor that is set up to indent exactly in whatever style.

    The style you display is much simpler and less demanding of the editor, and I think you should stick with it. The only inconsistency I can see is that you put the first do on its own line while you put the other dos after the then/else.

    Heed the other advice about how to think about code in Haskell, but stick to your indentation style.

    0 讨论(0)
  • 2021-01-31 09:47

    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.

    0 讨论(0)
提交回复
热议问题