How can unsafePerformIO be used to write unsafeCoerce?
Asked Answered
F

1

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?

Flax answered 9/7, 2021 at 20:57 Comment(0)
F
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 #-}
Flax answered 9/7, 2021 at 20:57 Comment(5)
I confess I chuckled at "the right way": we are creating an abomination, but hey, we should be careful not to do that in the wrong way! ;-)Barchan
I had trouble understanding "to coerce the coercion function" might it be better to write "to coerce the identity function" or "to coerce the identity function into a coercion function"?Flexed
@chi, yes, it is a bit silly, but I think fun.Flax
@Noughtmare, is it better now?Flax
Yes, I think it is a bit better now.Flexed

© 2022 - 2024 — McMap. All rights reserved.