Use of evaluate in Control.Concurrent.MVar

Control.Concurrent.MVar uses `evaluate` in a couple places, and I don't understand why: modifyMVar :: MVar a -> (a -> IO (a,b)) -> IO b modifyMVar m io = mask $ \restore -> do a <- takeMVar m (a',b) <- restore (io a >>= evaluate) `onException` putMVar m a putMVar m a' return b modifyMVarMasked :: MVar a -> (a -> IO (a,b)) -> IO b modifyMVarMasked m io = mask_ $ do a <- takeMVar m (a',b) <- (io a >>= evaluate) `onException` putMVar m a putMVar m a' return b The general purpose is to make sure that the result of the IO action is forced to head normal form within the scope of the `catch` block created by `onException`. But in this context, I don't see why we need to use `evaluate` rather than just `seq`, or why that is desirable. Couldn't we just do this? modifyMVar m io = mask $ \restore -> do a <- takeMVar m (a',b) <- restore (io a >>= (pure $!)) `onException` putMVar m a putMVar m a' return b David

Hi David, On 12/10/2020 21.01, David Feuer wrote:
Couldn't we just do this?
modifyMVar m io = mask $ \restore -> do a <- takeMVar m (a',b) <- restore (io a >>= (pure $!)) `onException` putMVar m a putMVar m a' return b
No — the haddocks for 'evaluate' in Control.Exception explain the difference between the two. In particular,
The rule of thumb is to use evaluate to force or handle exceptions in lazy values. If, on the other hand, you are forcing a lazy value for efficiency reasons only and do not care about exceptions, you may use return $! x.
If you want to know why, I think you can find some old bugs in the ghc bug tracker that resulted from an incorrect usage of return $! ... where evaluate was warranted. Roman

Yes, I generally understand that; I just don't *think* it applies
here. The issue with `seq` tends to be that strictness information can
propagate "back in time", or forcing can be delayed, etc. I just don't
immediately see that as an issue in this *particular* spot. That said,
I imagine that in many cases the compiler will see that the pair is
surely produced, in which case the `evaluate` will go away.
On Mon, Oct 12, 2020 at 2:20 PM Roman Cheplyaka
Hi David,
On 12/10/2020 21.01, David Feuer wrote:
Couldn't we just do this?
modifyMVar m io = mask $ \restore -> do a <- takeMVar m (a',b) <- restore (io a >>= (pure $!)) `onException` putMVar m a putMVar m a' return b
No — the haddocks for 'evaluate' in Control.Exception explain the difference between the two. In particular,
The rule of thumb is to use evaluate to force or handle exceptions in lazy values. If, on the other hand, you are forcing a lazy value for efficiency reasons only and do not care about exceptions, you may use return $! x.
If you want to know why, I think you can find some old bugs in the ghc bug tracker that resulted from an incorrect usage of return $! ... where evaluate was warranted.
Roman _______________________________________________ Libraries mailing list Libraries@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
participants (2)
-
David Feuer
-
Roman Cheplyaka