Greetings,

I was trying to understand the magic inside Data.Binary, and found two
somewhat suspicious uses of inlinePerformIO, which imho has a far too
innocuous name:

| toLazyByteString :: Builder -> L.ByteString
| toLazyByteString m = S.LPS $ inlinePerformIO $ do
|     buf <- newBuffer defaultSize
|     return (runBuilder (m `append` flush) (const []) buf)

Why is this safe?  Considering the GHC implementation of IO, isn't there
a real danger that 'newBuffer defaultSize' is floated out and therefore
every invocation of 'toLazyByteString' starts out with the same buffer?
Isn't that exactly the reason why unsafePerformIO and runST are declared
NOINLINE?

The other occurence is:

| unsafeLiftIO :: (Buffer -> IO Buffer) -> Builder
| unsafeLiftIO f =  Builder $ \ k buf -> inlinePerformIO $ do
|     buf' <- f buf
|     return (k buf')

which might be safe, since 'f buf' cannot float out of the lambda which
binds 'buf', but still, all this stuff is inlined, something constant
might get plugged in the place of buf and the result might be floated
out to give us an even harder to find Heisenbug.

Am I missing something and this is actually safe?  If not, what can be
done to avoid such errors?  I'd really hate to find building blocks that
crumble under pressure in standard libraries...


-Udo

Attachment: signature.asc
Description: Digital signature

_______________________________________________
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe

Reply via email to