Чтобы ознакомиться с unsafePerformIO
(как использовать его и когда его использовать), я реализовал модуль для генерации уникальных значений.
Вот что у меня есть:
module Unique (newUnique) where
import Data.IORef
import System.IO.Unsafe (unsafePerformIO)
-- Type to represent a unique thing.
-- Show is derived just for testing purposes.
newtype Unique = U Integer
deriving Show
-- I believe this is the Haskell'98 derived instance, but
-- I want to be explicit, since its Eq instance is the most
-- important part of Unique.
instance Eq Unique where
(U x) == (U y) = x == y
counter :: IORef Integer
counter = unsafePerformIO $ newIORef 0
updateCounter :: IO ()
updateCounter = do
x <- readIORef counter
writeIORef counter (x+1)
readCounter :: IO Integer
readCounter = readIORef counter
newUnique' :: IO Unique
newUnique' = do { x <- readIORef counter
; writeIORef counter (x+1)
; return $ U x }
newUnique :: () -> Unique
newUnique () = unsafePerformIO newUnique'
К моему удовольствию, в package называется Data.Unique
выбрал тот же тип данных, как и я; с другой стороны, они выбрали тип newUnique :: IO Unique
, но я хочу держаться подальше от IO
, если это возможно.
Является ли эта реализация опасной? Может ли это привести GHC изменить семантику программы, которая ее использует?