
On Thu, Apr 22, 2010 at 10:30 AM, Simon Marlow
Funnily enough, before posting the above message I followed exactly the line of reasoning you detail below to discover that there isn't a way to fix this using parametricity. It's useful to have it documented, though - thanks.
In their paper on "Lightweight Monadic Regions" [1] Oleg Kiseljov and Chung-chieh Shan had to solve a very similar problem. To quote the middle of section 4.3: "...Instead of leaking a handle, we may try to leak a computation involving the handle: do ac <- newRgn (do h2 <- newSHandle "fname2" ReadMode return (shGetLine h2)) ac This code is unsafe because newRgn closes the handle h2 when the child computation exits. Executing the action ac outside of newRgn would attempt to read from an already closed handle. Fortunately, this code raises a type error. The handle h2 has the type SHandle (IORT s m), where s is quantified in the type of newRgn. The computation shGetLine h2 therefore has the type (MonadRaise (IORT s m) m2, RMonadIO m2) => m2 String Since do-bindings are monomorphic, the type checker must resolve the two constraints above to infer a type for ac. Since m2 is unknown, constraint resolution fails and yields a type error. Indeed, every way to instantiate m2 that satisfies the suffix constraint MonadRaise (IORT s m) m2 mentions s, but the type of ac, which contains m2, cannot mention s because ac takes scope beyond s. It does not help to existentially quantify over s in the type of ac, because the type error would just shift to where ac is used..." If we look at: unmaskHack3 :: IO a -> IO a unmaskHack3 m = do mv <- newEmptyMVar tid <- forkIOWithUnmask $ \unmask -> putMVar mv (apply unmask m) unmaskedM <- takeMVar mv unmaskedM -- (or use join) the correspondence is clear: newRgn = forkIOWithUnnmask h2 = unmask shGetLine = apply ac = unmaskedM I'm hoping we can solve this in a similar way! regards Bas [1] http://okmij.org/ftp/Haskell/regions.html#light-weight