Ioref в Хаскелл
Мне было интересно, есть ли законное использование для выводе ioref в Haskell? Более конкретно, я был бы благодарен, если бы кто-то мог обратиться к следующему или указать соответствующее место, чтобы узнать больше об этом:
- считается ли использование IORef плохой практикой Haskell? Если да, то почему? Более конкретно, как это лучше или хуже, чем монада IO?
Если кто-то хотел добавить состояние в программу, не является ли монада состояния лучшим (более чистым) способом он. Если кто-то чувствует себя более обязанным, не может ли он все еще использовать STM и MVar, и все еще быть лучше?
существуют ли сценарии программирования, которые легко обрабатываются с помощью IORefs, а не STM, MVar или чистого ввода-вывода?
Я читаю статью, которая использует IORef для фрагментов кода, и мне было трудно читать из-за негативного восприятия, которое я имею к IORef. Вместо того, чтобы валяться в своем невежестве, я думал спросить моего товарища Haskellers за помощью может быть лучшей идеей.
1 ответов
во-первых, я думаю, что для фрагментов кода в документ, используя IORef
совершенно разумно, особенно если бумага не о рекомендации по изменяемым ссылкам или параллелизму. IORef
прост для понимания, имеет синтаксис и семантику straightfoward (особенно в непараллельной настройке) и является естественным выбором, если вы хотите, чтобы читатель сосредоточился на аспектах ваших примеров, отличных от IORef
s. К сожалению, авторский подход имеет backfired для вас - просто игнорируйте IORef
s и обратите внимание на то, что говорит остальная часть газеты.
(если бумага был о лучших практиках для изменяемых ссылок или параллелизма, возможно, это было написано до того, как были доступны лучшие альтернативы.)
в любом случае, к вашему большому вопросу, основные возражения против использования IORef
будет:
- как и любой другой механизм введения изменяемого состояния в вашу программу, это делает ваш код более сложным для рассуждения, обслуживания,тестирования и т. д. -- все обычные вещи, которые сторонники функционального программирования сказали бы, дают FP "преимущество" над мутационно-интенсивными императивными алгоритмами.
- это просто оболочка newtype вокруг специализированного
STRef RealWorld
, и единственное, что он добавляет болееSTRef
некоторые атомарные операции. В непараллельном коде нет веских причин не использоватьSTRef s
значенияST s
монада, так как они более гибкие - вы можете запустить их в чистом коде сrunST
или, если необходимо, в монаде IO сstToIO
. - в параллельном коде есть более мощные абстракции, такие как
MVar
иSTM
это гораздо проще в работе, чемIORef
s.
таким образом, в той мере, в какой изменяемое состояние "плохо" и-если вам это действительно нужно-лучшие альтернативы доступны в зависимости от того, нужен ли вам параллелизм или нет, не так много рекомендовать IORef
.
на другая рука, если вы уже работаете над каким-то непараллельным кодом в IO
монада, потому что вам нужно выполнять фактические операции ввода-вывода, и вам действительно нужно какое-то всепроникающее изменяемое состояние, которое нелегко отделить от ввода-вывода, а затем использовать IORef
s кажется законным.
что касается ваших более конкретных вопросов:
я думаю, было бы безопасно сказать, что с помощью
IORef
считается "плохой практикой", когда слабый инструмент сделал бы работу. Этот более слабый инструмент может бытьSTRef s
или еще лучшеState
монада или еще лучше переписанный алгоритм более высокого порядка, который вообще не нуждается в каком-либо состоянии. Потому чтоIORef
сочетает в себе IO с изменяемыми ссылками, это своего рода императивный кувалда, которая, вероятно, приведет к наиболее unidiomatic Haskell кода возможно, так что лучше избегать, если это "очевидно" правильное решение для конкретной проблемы.на
State
монада - обычно предпочтительный идиоматический способ добавления состояния в программу, но он обеспечивает "иллюзию" изменяемого состояния, пропуская последовательность неизменяемых значений состояния через вычисление, и не все алгоритмы могут быть эффективно реализованы таким образом. Где требуется истинное изменяемое состояние, anSTRef
обычно является естественным выбором в непараллельной установке. Обратите внимание, что вы, вероятно, не будете использоватьMVar
илиSTM
в непараллельной настройке - нет причин использовать их в этом случае, и они заставят тебя войти вIO
монада, даже если вам это не нужно.Да, есть сценарии программирования, где либо
IORef
илиSTRef
предпочтительнееState
,STM
,MVar
, или чистоIO
(см. ниже). Есть несколько сценариев, гдеIORef
явно предпочтительнееSTRef
, но-как уже упоминалось выше-если вы уже вIO
монада и потребность в истинном изменяемом состоянии, которое запутано с IO операции, тогдаIORef
вероятно, имеет преимущество передSTRef
С точки зрения немного более чистого синтаксиса.
некоторые примеры случаев, когда либо IORef
или STRef
хороший подход:
-
Data.Unique
наbase
пакет используетIORef
как глобальный счетчик для генерации уникальных объектов. - на
base
библиотека, внутренняя ручка файла широко используетIORef
s для крепления буферов к ручкам. Это хорошо пример "уже находясь в монаде IO с запутанными операциями IO". - многие векторные алгоритмы наиболее эффективно реализуются с использованием изменяемых векторов (например, даже что-то простое, как подсчет частоты байтов в блоке данных). Если вы используете изменяемые векторы из
vector
пакета, то технически вы используете изменяемые массивы байтов, а неSTRef
илиIORef
, но это все еще морально эквивалентно. - на
equivalence
пакет используетSTRef
s для эффективного реализация алгоритма union-join. - в качестве примера несколько на носу, если вы реализуете интерпретатор для императивного языка, а затем с помощью
IORef
илиSTRef
значения для изменяемых переменных, как правило, будет наиболее эффективным.