
On 07/04/2010 16:20, Sittampalam, Ganesh wrote:
Simon Marlow wrote:
I came to the conclusion that counting nesting layers doesn't solve the problem: the wormhole still exists in the form of nested unmasks. That is, a library function could always escape out of a masked context by writing
unmask $ unmask $ unmask $ ...
enough times. [...] mask :: ((IO a -> IO a) -> IO b) -> IO b mask io = do b<- blocked if b then io id else block $ io unblock
to be used like this:
a `finally` b = mask $ \restore -> do r<- restore a `onException` b b return r
So the property we want is that if I call a library function
mask $ \_ -> call_library_function
then there's no way that the library function can unmask exceptions. If all they have access to is 'mask', then that's true. [...] It's possible to mis-use the API, e.g.
getUnmask = mask return
Given that both the "simple" mask/unmask and your alternate proposal have backdoors, is the extra complexity really worth it?
The answer is yes, for a couple of reasons. 1. this version really is safer than mask/unmask that count nesting levels. If the caller is playing by the rules, then a library function can't unmask exceptions. The responsibility not to screw up is in the hands of the caller, not the callee: that's an improvement. 2. in this version more of the code is in Haskell, and the primitives and RTS implementation are simpler. So actually I consider this less complex than counting nesting levels. I did implement the nesting levels version first, and when adding non-interruptibility to the mix things got quite hairy. Cheers, Simon