Ioref в Хаскелл
Мне было интересно, есть ли законное использование для выводе ioref в Haskell? Более конкретно, я был бы благодарен, если бы кто-то мог обратиться к следующему или указать соответствующее место, чтобы узнать больше об этом:
- считается ли использование IORef плохой практикой Haskell? Если да, то почему? Более конкретно, как это лучше или хуже, чем монада IO?
Если кто-то хотел добавить состояние в программу, не является ли монада состояния лучшим (более чистым) способом он. Если кто-то чувствует себя более обязанным, не может ли он все еще использовать STM и MVar, и все еще быть лучше?
существуют ли сценарии программирования, которые легко обрабатываются с помощью IORefs, а не STM, MVar или чистого ввода-вывода?
Я читаю статью, которая использует IORef для фрагментов кода, и мне было трудно читать из-за негативного восприятия, которое я имею к IORef. Вместо того, чтобы валяться в своем невежестве, я думал спросить моего товарища Haskellers за помощью может быть лучшей идеей.
1 ответов
во-первых, я думаю, что для фрагментов кода в документ, используя IORef совершенно разумно, особенно если бумага не о рекомендации по изменяемым ссылкам или параллелизму. IORef прост для понимания, имеет синтаксис и семантику straightfoward (особенно в непараллельной настройке) и является естественным выбором, если вы хотите, чтобы читатель сосредоточился на аспектах ваших примеров, отличных от IORefs. К сожалению, авторский подход имеет backfired для вас - просто игнорируйте IORefs и обратите внимание на то, что говорит остальная часть газеты.
(если бумага был о лучших практиках для изменяемых ссылок или параллелизма, возможно, это было написано до того, как были доступны лучшие альтернативы.)
в любом случае, к вашему большому вопросу, основные возражения против использования IORef будет:
- как и любой другой механизм введения изменяемого состояния в вашу программу, это делает ваш код более сложным для рассуждения, обслуживания,тестирования и т. д. -- все обычные вещи, которые сторонники функционального программирования сказали бы, дают FP "преимущество" над мутационно-интенсивными императивными алгоритмами.
- это просто оболочка newtype вокруг специализированного
STRef RealWorld, и единственное, что он добавляет болееSTRefнекоторые атомарные операции. В непараллельном коде нет веских причин не использоватьSTRef sзначенияST sмонада, так как они более гибкие - вы можете запустить их в чистом коде сrunSTили, если необходимо, в монаде IO сstToIO. - в параллельном коде есть более мощные абстракции, такие как
MVarиSTMэто гораздо проще в работе, чемIORefs.
таким образом, в той мере, в какой изменяемое состояние "плохо" и-если вам это действительно нужно-лучшие альтернативы доступны в зависимости от того, нужен ли вам параллелизм или нет, не так много рекомендовать IORef.
на другая рука, если вы уже работаете над каким-то непараллельным кодом в IO монада, потому что вам нужно выполнять фактические операции ввода-вывода, и вам действительно нужно какое-то всепроникающее изменяемое состояние, которое нелегко отделить от ввода-вывода, а затем использовать IORefs кажется законным.
что касается ваших более конкретных вопросов:
я думаю, было бы безопасно сказать, что с помощью
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библиотека, внутренняя ручка файла широко используетIORefs для крепления буферов к ручкам. Это хорошо пример "уже находясь в монаде IO с запутанными операциями IO". - многие векторные алгоритмы наиболее эффективно реализуются с использованием изменяемых векторов (например, даже что-то простое, как подсчет частоты байтов в блоке данных). Если вы используете изменяемые векторы из
vectorпакета, то технически вы используете изменяемые массивы байтов, а неSTRefилиIORef, но это все еще морально эквивалентно. - на
equivalenceпакет используетSTRefs для эффективного реализация алгоритма union-join. - в качестве примера несколько на носу, если вы реализуете интерпретатор для императивного языка, а затем с помощью
IORefилиSTRefзначения для изменяемых переменных, как правило, будет наиболее эффективным.