Способ избежать общего использования unsafePerformIO
Я часто нахожу этот шаблон в коде на Haskell:
options :: MVar OptionRecord
options = unsafePerformIO $ newEmptyMVar
...
doSomething :: Foo -> Bar
doSomething = unsafePerformIO $ do
opt <- readMVar options
doSomething' where ...
По сути, у каждого есть запись опций или чего-то подобного, которая изначально устанавливается в начале программы. Поскольку программист ленив, он не хочет нести options
запись по всей программе. Он определяет MVar
чтобы сохранить его - определяется уродливым использованием unsafePerformIO
, Программист гарантирует, что состояние устанавливается только один раз и перед выполнением какой-либо операции. Теперь каждая часть программы должна использовать unsafePerformIO
опять же, просто чтобы извлечь варианты.
На мой взгляд, такая переменная считается прагматически чистой (не бейте меня). Существует ли библиотека, которая абстрагирует эту концепцию и гарантирует, что переменная установлена только один раз, то есть, что до этой инициализации не выполняется вызов, и что не нужно писать unsafeFireZeMissilesAndMakeYourCodeUglyAnd
DisgustingBecauseOfThisLongFunctionName
5 ответов
Если вы используете MVar для хранения настроек или чего-то подобного, почему бы вам не попробовать монаду для чтения?
foo :: ReaderT OptionRecord IO ()
foo = do
options <- ask
fireMissiles
main = runReaderT foo (OptionRecord "foo")
(И обычный Reader, если вам не требуется IO:P)
Те, кто обменял бы существенную ссылочную прозрачность на небольшое временное удобство, не заслуживают ни чистоты, ни удобства.
Это плохая идея. Код, в котором вы найдете это, является плохим кодом.*
Нет способа полностью обернуть этот шаблон безопасно, потому что это не безопасный шаблон. Не делайте этого в своем коде. Не ищите безопасный способ сделать это. Нет безопасного способа сделать это. Положить unsafePerformIO
медленно опустился на пол и отошел от консоли...
* Есть законные причины, по которым люди используют MVars верхнего уровня, но эти причины по большей части связаны с привязками к стороннему коду или несколькими другими вещами, где альтернатива очень грязная. В тех случаях, насколько я знаю, однако, MVars верхнего уровня не доступны из-за unsafePerformIO
,
Используйте неявные параметры. Они чуть менее тяжелы, чем каждая функция Reader
или же ReaderT
в своем роде. Вы должны изменить сигнатуры типов ваших функций, но я думаю, что такое изменение может быть записано в сценарии. (Сделал бы хорошую функцию для среды разработки Haskell.)
Я часто нахожу этот шаблон в коде на Haskell:
Прочитайте другой код.
Поскольку программист ленив, он не хочет нести запись опций по всей программе. Он определяет MVar, чтобы сохранить его - определяется уродливым использованием unsafePerformIO. Программист гарантирует, что состояние устанавливается только один раз и перед выполнением какой-либо операции. Теперь каждая часть программы должна снова использовать unsafePerformIO, просто чтобы извлечь параметры.
Похоже, буквально в точности то, что выполняет монада читателя, за исключением того, что монада читателя делает это безопасным способом. Вместо того, чтобы приспосабливать свою собственную лень, просто напишите настоящий хороший код.
Есть важная причина не использовать этот шаблон. Насколько я знаю, в
options :: MVar OptionRecord
options = unsafePerformIO $ newEmptyMVar
Haskell не дает никаких гарантий, что options
будет оцениваться только один раз. Так как результат option
это чистое значение, его можно запомнить и использовать повторно, но его также можно пересчитать для каждого вызова (т.е. встроенного), и значение программы не должно меняться (в отличие от вашего случая).
Если вы все еще решили использовать этот шаблон, обязательно добавьте {-# NOINLINE options #-}
иначе это может быть встроено, и ваша программа потерпит неудачу! (И этим мы отказываемся от гарантий, данных языком и системой типов, и полагаемся исключительно на реализацию конкретного компилятора.)
Эта тема широко обсуждалась, и возможные решения хорошо изложены на Хаскелле вики в изменяемом состоянии верхнего уровня. В настоящее время невозможно безопасно абстрагировать этот шаблон без дополнительной поддержки компилятора.