On Fri, 2007-06-15 at 01:03 +0200, Udo Stenzel wrote: > 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:
It's not really supposed to be a public api. We've decided to rename Data.ByteString.Base to .Internal to make that fact clearer. > | 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? Yes, that seems very suspicious to me. > 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. I think this is safe because of the continuation k. In the Builder monoid, the continuation takes the place of the IO state token that gets single threaded through everything. Duncan _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe