
Roberto Zunino wrote:
Floating out (newBuffer defaultSize) as in
| foo = newBuffer defaultSize | | toLazyByteString m = S.LPS $ inlinePerformIO $ do | buf <- foo | return (runBuilder (m `append` flush) (const []) buf)
would still be safe, AFAICS. Floating out buf instead should be prevented by the implicit RealWorld parameter.
That's actually what I meant, though I wouldn't describe it as "floating out buf", since that's not the only thing that happens, and it is not prevented. Unfolding a bit gives you something like | toLazyByteString m = S.LPS ( | case newBuffer defaultSize RealWorld# of { ( buf, world1 ) -> | runBuilder blahblah buf } ) since the scrutinee of the case expression is constant, it can float anywhere: | bufAndWorld1 = newBuffer defaultSize RealWorld# | | toLazyByteString m = S.LPS ( | case bufAndWorld1 of { (# buf, world1 #) -> | runBuilder blahblah buf } ) and that is bad. It might simplify even further, making the resulting bug even harder to detect. The only reason this doesn't happen with unsafePerformIO or runST is that it is not inlined. One of them even has a comment in the GHC library sources to the effect that the NOINLINE pragma was forgotten, which resulted in a subtle and ugly bug. -Udo -- If you cannot in the long run tell everyone what you have been doing, your doing was worthless. -- Erwin Schrödinger