Asynchronous exception wormholes kill modularity
ml at isaac.cedarswampstudios.org
Thu Apr 8 13:27:02 EDT 2010
On 04/08/10 04:23, Simon Marlow wrote:
> On 07/04/2010 18:54, Isaac Dupree wrote:
>> On 04/07/10 11:12, Simon Marlow wrote:
>>> It's possible to mis-use the API, e.g.
>>> getUnmask = mask return
>> unmask a = mask (\restore -> return restore) >>= (\restore -> restore a)
> That doesn't work, as in it can't be used to unmask exceptions when they
> are masked. The 'restore' you get just restores the state to its
> current, i.e. masked, state.
oh good point. Then you're right, anyone who was trying to work around
it would be doing something obviously wrong.
>>> mask :: ((IO a -> IO a) -> IO b) -> IO b
>> It needs to be :: ((forall a. IO a -> IO a) -> IO b) -> IO b
>> so that you can use 'restore' on two different pieces of IO if you need
>> to. (alas, this requires not just Rank2Types but RankNTypes. Also, it
>> doesn't cure the loophole. But I think it's still essential.)
> Sigh, yes I suppose that's true, but I've never encountered a case where
> I needed to call unmask more than once, let alone at different types,
> within the scope of a mask. Anyone else?
FWIW, the function I made up to test my theory was as below; I haven't
thought of any "actual" uses yet:
finally2 :: IO a1 -> IO a2 -> IO b -> IO (a1, a2)
finally2 a1 a2 b =
mask $ \restore -> do
r <- (liftM2 (,) (restore a1) (restore a2)) `onException` b
More information about the Libraries