1

How can reverse2lines transformed into the using >>= syntax ? Just like addOneInt is transformed into addOneInt'?

addOneInt :: IO () 
addOneInt = do line <- getLine
               putStrLn (show (1 + read line :: Int))      


addOneInt' :: IO ()
addOneInt' = getLine >>= \line ->
             putStrLn (show ( 1 + read line :: Int))  

reverse2lines :: IO () 
reverse2lines = 
 do line1 <- getLine 
    line2 <- getLine
    putStrLn (reverse line2) 
    putStrLn (reverse line1)

Please also consider reading the follow up question and its valuable answers.

Community
  • 1
  • 1
jhegedus
  • 20,244
  • 16
  • 99
  • 167

3 Answers3

5

You could safely deduce it from what you already know. Start by fully parenthesizing your addOneInt':

addOneInt' = getLine >>= (\line ->
             putStrLn (show (1 + read line :: Int)) )

Next, you reverse2lines can be equivalently written as

reverse2lines = 
 do { line1 <- getLine ;
      do { line2 <- getLine ;
           do { putStrLn (reverse line2) ;
                do { putStrLn (reverse line1) } } } }

Applying the one-step transformation that you already used for addOneInt, as much times as needed, you'd get

reverse2lines' :: IO ()
reverse2lines' = 
    getLine  >>=  (\line1 ->
      getLine  >>=  (\line2 ->
        putStrLn (reverse line2)  >>
          putStrLn (reverse line1) ) )

Syntactically, the parentheses around lambda expressions can be omitted, but writing them explicitly here helps to clarify and make obvious the nested structure of these functions, especially if we'd line up the indentation.

Full translation arranges for the monad's fail function to be called on pattern mismatch, but since the variable patterns (line1, line2) are irrefutable, this translation is in fact exact.

Will Ness
  • 70,110
  • 9
  • 98
  • 181
  • Thank you for the detailed answer, is it possible to formulate `reverse2lines` with only `>>=` ? Without `>>` ? – jhegedus Apr 07 '14 at 11:38
  • 2
    yes, `m >> n === m >>= (\_ -> n)`. For some monads it is even defined this way; for others a more efficient specific code is possible for `>>`. – Will Ness Apr 07 '14 at 11:43
  • 2
    nah, it's very simple: you just replace `>>` with `>>= \_ ->`. :) – Will Ness Apr 07 '14 at 11:49
  • So basically every new command in the sequence goes into the belly of a new lambda ? – jhegedus Apr 07 '14 at 12:27
  • 1
    (sorry, I was offline) yes, exactly. That's the special thing about the monads, too: because they are created nested, each can branch on its input, and decide which of the possibilities to take (as in `if pred then do this else do that`). You could make them non-nested too, with explicit use of bind (`>>=`), but then you'd have to specifically arrange for passing the needed parts of info along the line, unlike in nested setting where the outer variables are just available in the inner functions. – Will Ness Apr 07 '14 at 20:35
  • There some slight logical inconstancy with this answer as discussed here: http://stackoverflow.com/questions/23090948/nested-do-syntax?noredirect=1#comment35293452_23090948 – jhegedus Apr 15 '14 at 18:34
  • @jhegedus I think my answer here and Tikhon's there are exactly equivalent. So at least there isn't any inconsistency, thanks goodness. You just wanted additional explanation about why the nested do expressions were equivalent to the non-nested one. Yes, I didn't try to explain it, I used it as a self-evident equivalency, mostly because `do {one}` == `one`. So `do {one; do {another}}` == `do {one; another}`. Admittedly, this doesn't cover the `do {one; do{ two; three}}` == `do { one; two; three }`. I skipped that part. :) – Will Ness Apr 15 '14 at 18:51
  • 1
    Yes, thanks for the answer, indeed there is nothing incorrect about the answer, I understand that now. I am quite a beginner in Haskell and get confused easily but thanks to SOF I can get un-confused again :) – jhegedus Apr 15 '14 at 19:11
  • I see now what you meant by inconsistency. that equivalency indeed is a consequence of the `do` translation rules; so without knowing them you indeed had no way of knowing about that equivalency, even if it looks so self-evident to me. Good for you for insisting on proper chain of evidence and derivation! :) -- for me, it was an application of recursive thinking, but the fact that it indeed *can* be applied here is a separate issue. (re: [do { e; *stmts* } == e >> do { *stmts* } == do { e; do { *stmts* }}](http://www.haskell.org/onlinereport/haskell2010/haskellch3.html#x8-470003.14) etc. ). – Will Ness Apr 15 '14 at 19:16
  • @jhegedus and I'm not the only one to make that leap: [haskellwiki page on monad laws](http://www.haskell.org/haskellwiki/Monad_laws) too shows what amounts to `do { a; do { b; c}}` == `do {a; b; c;}`, under "associativity" in "practical meaning" subsection, without any explicit justification. :) – Will Ness Apr 15 '14 at 19:28
  • I just have this deep need to understand things from first principles :) but now is all sorted out. – jhegedus Apr 15 '14 at 20:49
4

Statements of type x <- m can be translated as m >>= \x -> ..., and statements in which the return value isn't bound like m can be translated as m >> ...

reverse2lines :: IO () 
reverse2lines = getLine 
                >>= \line1 -> getLine 
                >>= \line2 -> putStrLn (reverse line2)
                >> putStrLn (reverse line1)

The translation isn't exact - the fail monad function complicates things a little. You can read about it here, but as far as I know it's irrelevant when dealing with the IO monad.

Community
  • 1
  • 1
Benesh
  • 3,398
  • 1
  • 18
  • 38
1

Here is the code from Will's very nice answer using only >>= :

reverse2lines' :: IO () 
reverse2lines' = 
    getLine >>= (\line1 -> 
        getLine >>= (\line2 -> 
            putStrLn (reverse line2) >>= (\_ ->
               putStrLn (reverse line1) )))

Lesson learned : the sequencing in do corresponds simply to nesting of lambda expressions.

jhegedus
  • 20,244
  • 16
  • 99
  • 167