Using unsafePerformIO safely?

As long as the IO action is actually really pure (e.g. running another Haskell program that only uses interact in its main function), then no special measures are necessary. Things like NOINLINE, -fno-cse, and -fno-full-laziness are only required if you still want the side-effects of each unsafePerformIO call to be performed exactly once.

I’m imagining a context like

E[x] = do evaluate x; readFile "test"

This shows that writeFile "test" "something" is not pure, because do evaluate (unsafePerformIO (writeFile "test" "something")); readFile "test" is clearly different than do evaluate (); readFile "test". However, this does require some kind of equality on the IO type.

How else would we prove that writeFile "test" "something" is not pure?

Exceptions can only be caught from inside IO. In the pure subset of Haskell throwing an exception is like entering an infinite loop. Exceptions thrown from within the unsafely performed IO are no different. Catching exceptions inside the unsafePerformIO actually seems kind of strange to me, is the following function pure?

f x = unsafePerformIO (evaluate (x `seq` True) `catch` (\e -> (e :: SomeException) `seq` pure False))

That’s a good question.

1 Like