Convert a “do” notation with more than two actions

2019-02-22 06:37发布

I know that the following "do" notation's "bind" function is equivalent to getLine >>= \line -> putStrLn

do line <- getLine
   putStrLn line

But how is the following notation equivalent to bind function?

do line1 <- getLine
   putStrLn "enter second line"
   line2 <- getLine
   return (line1,line2)

4条回答
Rolldiameter
2楼-- · 2019-02-22 06:48

For this specific example you can actually avoid both do and >>= by using combinators from Control.Applicative:

module Main where
import Control.Applicative ((<$>), (<*>), (<*))

getInput :: IO (String, String)
getInput = (,) <$> getLine <* putStrLn "enter second line" <*> getLine

main = print =<< getInput

Which works as expected:

travis@sidmouth% ./Main
hello
enter second line
world
("hello","world")

It looks a little weird at first, but in my opinion the applicative style feels very natural once you're used to it.

查看更多
聊天终结者
3楼-- · 2019-02-22 06:51

I take it you are trying to see how to bind the result of "putStrLn". The answer is in the type of putStrLn:

putStrLn :: String -> IO ()

Remember that "()" is the unit type, which has a single value (also written "()"). So you can bind this in exactly the same way. But since you don't use it you bind it to a "don't care" value:

getLine >>= \line1 ->
putStrLn "enter second line" >>= \_ ->
getline >>= \line2 ->
return (line1, line2)

As it happens, there is an operator already defined for ignoring the return value, ">>". So you could just rewrite this as

getLine >>= \line1 ->
putStrLn "enter second line" >>
getline >>= \line2 ->
return (line1, line2)

I'm not sure if you are also trying to understand how bind operators are daisy-chained. To see this, let me put the implicit brackets and extra indentation in the example above:

getLine >>= (\line1 ->
   putStrLn "enter second line" >> (
      getline >>= (\line2 ->
         return (line1, line2))))

Each bind operator links the value to the left with a function to the right. That function consists of all the rest of the lines in the "do" clause. So the variable being bound through the lambda ("line1" in the first line) is in scope for the whole of the rest of the clause.

查看更多
The star\"
4楼-- · 2019-02-22 06:58
getLine >>= \line1 ->
putStrLn "enter second line" >>
getLine >>= \line2 ->
return (line1, line2)

Generally foo <- bar becomes bar >>= \foo -> and baz becomes baz >> (unless it's the last line of the do-block, in which case it just stays baz).

查看更多
在下西门庆
5楼-- · 2019-02-22 07:01

I would strongly suggest you to read the chapter Desugaring of Do-blocks in the book Real-World haskell. It tells you, that you all are wrong. For a programmer, it's the natural way to use a lambda, but the do-block is implemented using functions which - if a pattern maching failuire occurs - will call the fail implementation of the according monad.

For instance, your case is like:

let f x =
        putStrLn "enter second line" >>
        let g y = return (x,y)
            g _ = fail "Pattern mismatched"
        in getLine >>= g
    f _ = fail "Pattern mismatched"
in getLine >>= f

In a case like this, this may be completely irrelevant. But consider some expression that involves pattern-matching. Also, you can use this effect for some special stuff, eg, you can do something like this:

oddFunction :: Integral a => [a] -> [a]
oddFunctiond list = do
  (True,y) <- zip (map odd list) list
  return y

What will this function do? You can read this statement as a rule for working with the elements of the list. The first statement binds an element of the list to the var y, but only if y is odd. If y is even, a pattern matching failure occurs and fail will be called. In the monad instance for Lists, fail is simply []. Thus, the function strips all even elements from the list.

(I know, oddFunction = filter odd would do this better, but this is just an example)

查看更多
登录 后发表回答