Способ избежать общего использования unsafePerformIO - PullRequest
16 голосов
/ 20 мая 2011

Я часто нахожу этот шаблон в коде 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 ]

20 голосов
/ 20 мая 2011

Те, кто обменял бы существенную ссылочную прозрачность на небольшое временное удобство, не заслуживают ни чистоты, ни удобства.

Это плохая идея.Код, в котором вы находите это, является плохим кодом. *

Нет способа полностью обернуть этот шаблон безопасно, потому что это не безопасный шаблон.Не делайте этого в своем коде.Не ищите безопасный способ сделать это.Нет безопасного способа сделать это.Положите unsafePerformIO вниз на пол, медленно и отойдите от консоли ...

* Есть законные причины, по которым люди используют MVars высшего уровня, но эти причины связаны с привязками к иностраннымкод по большей части, или несколько других вещей, где альтернатива очень грязная.В этих случаях, насколько мне известно, MVars верхнего уровня не доступны из-за unsafePerformIO.

9 голосов
/ 20 мая 2011

Если вы используете MVar для хранения настроек или чего-то подобного, почему бы вам не попробовать монаду ридера?

foo :: ReaderT OptionRecord IO ()
foo = do
    options <- ask
    fireMissiles

main = runReaderT foo (OptionRecord "foo")

(и обычного ридера, если вам не требуется IO: P)

5 голосов
/ 20 мая 2011

Используйте неявные параметры. Они чуть менее тяжелые, чем у каждой функции Reader или ReaderT в своем типе. Вы должны изменить сигнатуры типов ваших функций, но я думаю, что такое изменение может быть записано в сценарии. (Будет хорошая функция для среды разработки Haskell.)

2 голосов
/ 24 мая 2014

Есть важная причина не использовать этот шаблон. Насколько я знаю, в

options :: MVar OptionRecord
options = unsafePerformIO $ newEmptyMVar

Haskell не дает никаких гарантий, что options будет оцениваться только один раз. Поскольку результат option является чистым значением, его можно запомнить и использовать повторно, но его также можно пересчитать для каждого вызова (т. Е. Встроенного), и значение программы не должно меняться (в отличие от вашего случая).

Если вы все еще решите использовать этот шаблон, обязательно добавьте {-# NOINLINE options #-}, в противном случае он может оказаться встроенным, и ваша программа завершится ошибкой! (И этим мы отказываемся от гарантий, данных языком и системой типов, и полагаемся исключительно на реализацию конкретного компилятора.)

Эта тема широко обсуждалась, и возможные решения хорошо изложены на Haskell Wiki в Изменяемое состояние верхнего уровня . В настоящее время невозможно безопасно абстрагировать этот шаблон без дополнительной поддержки компилятора.

1 голос
/ 14 апреля 2018

Я часто нахожу этот шаблон в коде на Haskell:

Читайте другой код.

Поскольку программист ленив, он не хочет нестипараметры записи по всей программе.Он определяет MVar, чтобы сохранить его - определяется уродливым использованием unsafePerformIO.Программист гарантирует, что состояние устанавливается только один раз и перед выполнением какой-либо операции.Теперь каждая часть программы должна снова использовать unsafePerformIO просто для извлечения опций.

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

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