Confusion with Haskell do blocks

I have the following code:

doSomething :: [Int] -> [Int]
doSomething arg = arg ++ [1]

afterThreeTurns = do
    first <- ["test"]
    doSomething [1] -- COMMENT THIS
    return first

      

This returns:

*Main> afterThreeTurns
["test","test"]

      

If I cross out the line labeled COMMENT THIS, it returns ["test"] as expected. What for? How do I see that it should Something shouldn't affect the first?

+3


source to share


3 answers


Since doSomething [1]

[2,1]

, your code is equivalent to:

afterThreeTurns = do
   first <- ["test"]
   x <- [2,1]
   return first

      

This is the same as list comprehension [ first | first <- ["test"], x <- [2,1] ]

, which explains why you are getting a list of length 2.

Note that the variable is x

not specified anywhere, so it can also be written:

afterThreeTurns = do
   first <- ["test"]
   _ <- [2,1]
   return first

      

Here's a similar case using a monad IO

. Code:



thirdLine = do
  getLine
  getLine
  x <- getLine
  putStrLn $ "The third line is: " ++ x

      

matches:

thirdLine = do
  _ <- getLine
  _ <- getLine
  x <- getLine
  putStrLn $ "The third line is: " ++ x

      

You can force ghc to flag these types of monadic operators -fwarn-unused-do-bind

. In your example, ghc will issue a warning:

...: Warning:
    A do-notation statement discarded a result of typeInt
    Suppress this warning by saying ‘_ <- doSomething [1]’
    or by using the flag -fno-warn-unused-do-bind

      

+6


source


Lets turn this into equivalent calls to >>=

:

["test"] >>= (\first ->
                 doSomething [1] >>= (\_ -> return first))

      

The compiler always does this with do

. The two recording methods are exactly the same.

Now >>=

for is the []

same as concatMap

the reversed arguments, so let's go ahead and do this transformation (and apply the definition return x = [x]

) and decrease:



concatMap (\first -> concatMap (\_ -> [first]) (doSomething [1])) ["test"]
concatMap (\first -> concatMap (\_ -> [first]) ([1] ++ [1])) ["test"]
concatMap (\first -> concatMap (\_ -> [first]) [1, 1]) ["test"]
concatMap (\first -> concat [[first], [first]]) ["test"]
concatMap (\first -> [first, first]) ["test"]
concat [["test"], ["test"]]
["test", "test"]

      

Intuitively, a Monad []

can be thought of as representing a "non-deterministic" computation (that is, a computation that can take one of several different possible outcomes). When you combine two non-deterministic computations in this way, the number of results is multiplied. This is due to the different "paths" that can be taken, one for each feature in each branch.

For reference, here are the conversions between do

and >>=

and >>

(note that this m1 >> m2

should always be equivalent m1 >>= (\_ -> m2)

):

do x <- m1                m1 >>= (\x ->
   m2 x                              m2 x
   ...                               ...)

do m1                     m1
   m2                     >> m2

do x                      x

      

+3


source


In a monad List

(or []

) "effect" returns multiple results instead of one. So your function doSomething

does not contribute to the result, but it does affect the effect, thus changing the length of the final list.

0


source







All Articles