23

Here's an extract from the documentation of evaluate:

Control.Exception.Base.evaluate :: a -> IO a
evaluate x

is not the same as

return $! x

A correct definition is

evaluate x = (return $! x) >>= return

(source)

These seem to have the same meaning. What is the difference between these two definitions?

Will Ness
  • 70,110
  • 9
  • 98
  • 181
HNoob
  • 591
  • 4
  • 13
  • 5
    Doesn't this actually violate the monad laws? – leftaroundabout Mar 04 '12 at 12:32
  • 3
    @leftaroundabout No, it doesn't. Both behave exactly the same if run, but if you `seq` the expressions, `return $! x` has a `seq` outermost, while `(return $! x) >>= return` has a `(>>=)` outermost. – Daniel Fischer Mar 04 '12 at 14:18
  • 4
    @leftaroundabout: No, because ⊥ is ignored for the purposes of laws. Standard monads like `Reader` behave the same way. (I don't buy Daniel Fischer's argument (which I've heard before from others), because "behave exactly the same if run" isn't really a well-defined concept.) – ehird Mar 04 '12 at 15:57
  • @DanielFischer I am not sure whether your statement is correct. `\`seq\`` has lower precedence than `>>=` - how can `>>=` be outermost? – mucaho Jun 10 '15 at 22:03
  • 2
    @mucaho I was speaking of the two expressions that would become the argument of `seq`. In `seq (return $! x)`, the `return $! x` has a `seq` [from `$!`] outermost, while in `seq ((return $! x) >>= return)`, the `(return $! x) >>= return` has a `>>=` outermost. – Daniel Fischer Jun 11 '15 at 04:31

1 Answers1

21

Quick ref:

The type of evaluate is:

evaluate :: a -> IO a

seq has the type a -> b -> b. It firstly evaluates the first argument, then returns the second argument.

Evaluate follows these three rules:

evaluate x `seq` y    ==>  y
evaluate x `catch` f  ==>  (return $! x) `catch` f
evaluate x >>= f      ==>  (return $! x) >>= f

The difference between the return $! x and (return $! x) >>= return becomes apparent with this expression:

evaluate undefined `seq` 42

By the first rule, that must evaluate to 42.

With the return $! x definition, the above expression would cause an undefined exception. This has the value ⊥, which doesn't equal 42.

With the (return $! x) >>= return definition, it does equal 42.

Basically, the return $! x form is strict when the IO value is calculated. The other form is only strict when the IO value is run and the value used (using >>=).

See this mailing list thread for more details.

I. J. Kennedy
  • 24,725
  • 16
  • 62
  • 87
David Miani
  • 14,518
  • 2
  • 47
  • 66
  • Why is `(return $! undefined) >>= return` different from `return $! undefined`? My guess is the former introduces one additional layer of indirection which prevents `undefined` from being evaluated in `((return $! undefined) >>= return) \`seq\` 42`. – mucaho Jun 10 '15 at 22:05