Asynchronous exception wormholes kill modularity

Isaac Dupree 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
>>
>> ...incidentally,
>> 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
     b
     return r

-Isaac


More information about the Libraries mailing list