2

In this code:

 fromMaybe <$> (print "A" >> return True) <*> (print "B" >> (return $ Just False))
 fromMaybe <$> (print "A" >> return True) <*> (print "B" >> (return $ Nothing))

I expected that due to laziness, either "A" or "B" would be printed depending on whether I supply Just something or Nothing but instead both are printed no matter what. Can someone explain a) what is going on exactly here? and b) how can I achieve the effect I want?

Drew
  • 12,578
  • 11
  • 58
  • 98
  • 2
    http://stackoverflow.com/questions/17409260/what-advantage-does-monad-give-us-over-an-applicative/17412969 – Carl Jul 09 '13 at 18:38

2 Answers2

6

Focusing on (print "B" >> (return $ Just False)) you're sequencing a print command with a return in the IO Monad. Since it's a Monad, it needs to evaluate print "B" exactly enough to get the "value" (even though it's just ignored) before being able to evaluate the return statement. For print in the IO monad that means performing the side effect.

This sequencing occurs in both of the IO arguments and so before they're able to be passed to the pure computation, fromMaybe, all of the effects have already been executed. Applicatives always execute all of the effects first and then compute the pure computation on the pure values.

fromMaybe True <$> case thing of
  Just _  -> print "A" >> return thing
  Nothing -> print "B" >> return thing

or maybe fromMaybe True <$> when (isJust thing) (print "A") >> print "B" >> return thing if that's better behavior.

J. Abrahamson
  • 72,246
  • 9
  • 135
  • 180
3

The following happens:

You map fromMaybe over an IO value. Hence the left part

fromMaybe <$> (print "A" >> return True) 

is an IO action that could be rewritten thus

print "A" >> return (fromMaybe True) :: IO (Maybe Bool -> Bool)

This means that the "A" will be printed no matter what.

Note that the IO monad is all about sequencing actions, hence an action later in the >>= chain can never affect whether earlier actions are executed.

Consider

fromMaybe <$> (getChar >>= return) 

It is clear that the Char the fromMaybe is applied to must come from actually reading a character. It is not the case that the character will only be read when it is needed.

If this were so, the following code would not make sense:

do
    a <- getChar
    b <- getChar
    -- at this point, a and b have been actually read from stdin already
    return (a < b)

For, it is not known whether (<) evaluates its left or right argument first.

Rather, in any case, a gets the value of the first character read and b that of the second. And the meaning of the code snippet is, accordingly, to read two characters and to check whether the first character read is lower than the second.

Indeed, if an IO action would be executed only when its value is actually needed, many programs wouldn't print anything, as it stands. This is because code like

 print "A" >> print "B"

deliberately ignores the result of the first print.

For the same reason, the "B" will always be printed.

Ingo
  • 36,037
  • 5
  • 53
  • 100