6

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?

dfeuer
  • 48,079
  • 5
  • 63
  • 167

1 Answers1

8

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 #-}
dfeuer
  • 48,079
  • 5
  • 63
  • 167