[Haskell-cafe] unsafeInterleaveIO respecting order of actions
Henning Thielemann
lemming at henning-thielemann.de
Sat Jan 3 05:31:09 EST 2009
On Thu, 1 Jan 2009, Brandon S. Allbery KF8NH wrote:
> On 2009 Jan 1, at 16:44, Henning Thielemann wrote:
>> If it is generally possible to use unsafeInterleaveIO such that it
>> executes actions in the right order, wouldn't this allow the definition
>> of a general lazy IO monad?
>
> I thought unsafeInterleaveIO and users of it (readFile, hGetContents) didn't
> guarantee the order of actions relative to independent IO actions (that is,
> those performed outside the unsafeInterleaveIO) and this was why it is
> generally disrecommended. For example the recurring situation where people
> try to readFile f >>= writeFile . someTransform and the writeFile fails with
> a "file locked" exception.
Sure, it's dangerous. But for what I want to do, this situation cannot
occur. I can come up with a simple example which might be generalized. It
simulates what hGetContents does.
liftLazy2 :: (a -> b -> c) -> IO a -> IO b -> IO c
liftLazy2 f x y =
fmap (\ ~(xr, ~(yr,())) -> f xr yr) $
unsafeInterleaveIO $ liftM2 (,) x $
unsafeInterleaveIO $ liftM2 (,) y $
return ()
test0, test1 :: IO String
test0 = liftLazy2 (const) getLine getLine
test1 = liftLazy2 (flip const) getLine getLine
test0 only requests the first line,
test1 expects two lines as user input.
However, with liftLazy2 we have only Applicative functionality, not Monad,
and it is not composable.
For example:
fmap (\((x,y),z) -> z) $ liftLazy2A (,) (liftLazy2A (,) getLine getLine) getLine
This requests only one line, but should three ones. The reason is that the
first two getLines are defered even until the last one. Thus, it is not
enough that liftLazy2 returns (IO c). Instead it must return (IO
(c,(a,(b,())))) and these pair emulated lists must somehow be combined in
order to preserve the order of execution. This looks somehow like a writer
monad transformer.
More information about the Haskell-Cafe
mailing list