Во-первых, я думаю, что для фрагментов кода в документе использование IORef
совершенно разумно, особенно если в статье не содержится о лучших методах для изменяемых ссылок или параллелизма.IORef
прост для понимания, имеет простой синтаксис и семантику (особенно в непараллельных настройках) и является естественным выбором, если вы хотите, чтобы читатель сосредоточился на аспектах ваших примеров, отличных от IORef
s.Очень жаль, что авторский подход обернулся для вас неудачей - просто проигнорируйте IORef
s и обратите внимание на то, что говорит остальная часть статьи.
(Если статья была о передовой практике для изменяемых ссылок или параллелизма, возможно, она была написана до того, как стали доступны лучшие альтернативы.)
В любом случае, по вашему более крупному вопросу, основные возражения против использования IORef
будут:
- Как и любой другой механизм введения изменяемого состояния в вашу программу, он делает ваш код более трудным для рассуждения, сопровождения, тестирования и т. Д. - все обычные вещи, которые сторонники функционального программирования сказали бы, дают FP «преимущество» надОбязательные для мутаций императивные алгоритмы.
- Это просто оболочка нового типа вокруг специализированной
STRef RealWorld
, и единственное, что она добавляет к STRef
, - это некоторые атомарные операции.В неконкурентном коде нет веской причины не использовать значения STRef s
в монаде ST s
, поскольку они более гибкие - вы можете запустить их в чистом коде с помощью runST
или, если необходимо, вМонада ввода-вывода с stToIO
. - В параллельном коде есть более мощные абстракции, такие как
MVar
и STM
, с которыми гораздо проще работать, чем с IORef
с.
Таким образом, в той степени, в которой изменяемое состояние является «плохим» и - если вам это действительно нужно - доступны лучшие альтернативы в зависимости от того, нужен ли вам параллелизм или нет, рекомендовать IORef
особо нечего.
С другой стороны, , если вы уже работаете с неконкурентным кодом в монаде IO
, потому что вам нужно выполнять реальные операции ввода-вывода, и вам действительно нужны некоторые распространяемые изменяемыезаявить, что нелегко отделиться от IO, тогда использование IORef
s кажется законным.
По поводу ваших более конкретных вопросов:
Я думаю, этобыло бы с уверенностью сказать, что с помощьюIORef
считается "плохой практикой", когда более слабый инструмент справился бы с работой .Этот более слабый инструмент может быть STRef s
или, что еще лучше, State
монадой или, что еще лучше, переписанным алгоритмом высшего порядка, который вообще не нуждается в каком-либо состоянии.Поскольку IORef
объединяет ввод-вывод с изменяемыми ссылками, это своего рода императивный кувалдой, который, вероятно, приведет к самому недиоматичному коду на Haskell, поэтому его лучше избегать, если только это "явно" не является правильным решением для конкретной проблемы.
Монада State
обычно является предпочтительным идиоматическим способом добавления состояния в программу, но она обеспечивает «иллюзию» изменчивого состояния, пропуская последовательность значений неизменяемого состояния посредством вычисления, а невсе алгоритмы могут быть эффективно реализованы таким образом.Там, где требуется истинное изменяемое состояние, STRef
обычно является естественным выбором в непараллельных условиях.Обратите внимание, что вы, вероятно, не будете использовать MVar
или STM
в непараллельных настройках - в этом случае нет причин использовать их, и они приведут вас к монаде IO
, даже если вы этого не сделалив противном случае это нужно.
Да, существуют сценарии программирования, в которых IORef
или STRef
предпочтительнее State
, STM
, MVar
или чисто IO
(см. Ниже)).Существует несколько сценариев, в которых IORef
явно предпочтительнее STRef
, но, как уже упоминалось выше, если вы уже находитесь в монаде IO
и нуждаетесь в истинно изменяемом состоянии, которое запутано в операциях ввода-вывода, тогдаIORef
, вероятно, имеет преимущество над STRef
с точки зрения немного более чистого синтаксиса.
Некоторые примеры случаев, когда IORef
или STRef
- хороший подход:
Data.Unique
в пакете base
использует IORef
в качестве глобального счетчика для генерации уникальных объектов. - В библиотеке
base
внутренние элементы дескриптора файла широко используют IORef
s для прикрепления буферов к ручкам.Это хороший пример того, что «уже в монаде ввода-вывода с запутанными операциями ввода-вывода». - Многие векторные алгоритмы наиболее эффективно реализуются с использованием изменяемых векторов (например, даже таких простых, как подсчет байтовых частот в блокеданные).Если вы используете изменяемые векторы из пакета
vector
, то технически вы используете изменяемые байтовые массивы, а не STRef
или IORef
, но это все еще морально эквивалентно. - Пакет
equivalence
использует STRef
s для эффективной реализации алгоритма union-join. - В качестве примера, если вы используете интерпретатор для императивного языка, тогда используйте
IORef
или * 1104.* значения для изменяемых переменных, как правило, будут наиболее эффективными.