I don't think this advice is quite right: say you do
(return $! some_big_thunk) >> throw e
GHC is within its rights to skip evaluating some_big_thunk, but maybe you wanted to force the thunk, and THEN throw an error. Of course, you'll do fine if you have:
(return $! some_big_thunk) >> throwIO e
So it might still be a useful rule of thumb.
Good point, I hadn't thought of that.
On the other hand, in your example it may actually be beneficial to discard the thunk, as that particular reference to it will be lost immediately. So, when there's no other reference to it (which is likely, although not necessary), we'll win time and lose nothing.
Another thing to consider is that the scenario you describe may happen in any monad, while we only have the luxury of 'evaluate' in IO. If this concern is real, should we recommend using pseq instead of seq?
It's more a matter of predictability. For example, would you rather a library function say, "When this function is evaluated, its argument is evaluated to WHNF" or "When this function is evaluated, AND there is no existing entry in the map for k, its argument is evaluated." This is important for parallel computation, because I care a lot about whether or not a thunk is evaluated or not. (Unsurprisingly, parallel computation is when one tends to recommend use of pseq.)
It's an interesting question whether or not pseq is a suitable replacement for evaluate. It might be.
I see the discussion now spans ghc-devs, the ticket *and* here. Perhaps it makes sense to move it back to ghc-devs so that others can more easily follow? Anyway, the new docs are a significant improvement already, so I'm accepting the revision.