Насколько безопасно `unsafePerformIO (newTVarIO 0)`?

Я заметил эту идиому в Data.Unique:

uniqSource :: TVar Integer
uniqSource = unsafePerformIO (newTVarIO 0)
{-# NOINLINE uniqSource #-}

Гарантируется ли запуск только один раз?

Ответ 1

В GHC да. 1 Подробнее см. документацию; существует вариант unsafeDupablePerformIO, который может выполняться несколько раз, что позволяет избежать накладных расходов, связанных с достижением этой гарантии.

Обратите внимание, что unsafePerformIO для создания изменяемых переменных вообще небезопасно; как описано в документации, вы можете создать полиморфную ссылку и использовать ее для реализации unsafeCoerce. Это не то, что вы, вероятно, сделаете случайно, однако, и это не относится к рассматриваемому коду (поскольку тип ссылки указан явно).

Пакет safe-globals абстрагирует эту "идиому" (полезную в некоторых случаях, она обычно считается антипаттерном и не должен использоваться в нормальном коде) таким образом, чтобы обеспечить безопасность.

Смотрите также мой предыдущий ответ на unsafePerformIO и предостережение, которое необходимо использовать при его применении.

1 Я уверен, что это относится ко всем другим реализациям; особая забота GHC, чтобы избежать повторного выполнения, необходима только в поточной настройке, и я не знаю никаких других потоковых реализаций Haskell. GHC является единственной реализацией, которую люди действительно используют в наши дни, хотя...