It's widely understood that unsafePerformIO
is not type safe. This is typically demonstrated by using it to implement unsafeCoerce
:
box :: IORef a
box = unsafePerformIO (newIORef undefined)
{-# NOINLINE box #-}
unsafeCoerce :: a -> b
unsafeCoerce a = unsafePerformIO $
writeIORef box a >> readIORef box
As I showed a few years ago, this implementation is not thread-safe. One thread could write to the box, and then another thread could write to the box again before the first thread can read. Oops! How can this be fixed?
As I showed once upon a time, the right way to do this is to use coercion through an IORef
to produce the unsafeCoerce
function itself, rather than to produce individual results of its application.
box :: IORef x
box = unsafePerformIO (newIORef undefined)
-- This NOINLINE is essential. If this binding is inlined,
-- then unsafeCoerce = undefined.
{-# NOINLINE box #-}
unsafeCoerce :: a -> b
unsafeCoerce = unsafePerformIO $
writeIORef box id >> readIORef box
-- Inlining this wouldn't break anything,
-- but it'd waste time with unnecessary IORef operations.
{-# NOINLINE unsafeCoerce #-}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With