Ioref в Хаскелл

Мне было интересно, есть ли законное использование для выводе ioref в Haskell? Более конкретно, я был бы благодарен, если бы кто-то мог обратиться к следующему или указать соответствующее место, чтобы узнать больше об этом:

  1. считается ли использование IORef плохой практикой Haskell? Если да, то почему? Более конкретно, как это лучше или хуже, чем монада IO?
  2. Если кто-то хотел добавить состояние в программу, не является ли монада состояния лучшим (более чистым) способом он. Если кто-то чувствует себя более обязанным, не может ли он все еще использовать STM и MVar, и все еще быть лучше?

  3. существуют ли сценарии программирования, которые легко обрабатываются с помощью 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 кажется законным.

что касается ваших более конкретных вопросов:

  1. я думаю, было бы безопасно сказать, что с помощью IORef считается "плохой практикой", когда слабый инструмент сделал бы работу. Этот более слабый инструмент может быть STRef s или еще лучше State монада или еще лучше переписанный алгоритм более высокого порядка, который вообще не нуждается в каком-либо состоянии. Потому что IORef сочетает в себе IO с изменяемыми ссылками, это своего рода императивный кувалда, которая, вероятно, приведет к наиболее unidiomatic Haskell кода возможно, так что лучше избегать, если это "очевидно" правильное решение для конкретной проблемы.

  2. на State монада - обычно предпочтительный идиоматический способ добавления состояния в программу, но он обеспечивает "иллюзию" изменяемого состояния, пропуская последовательность неизменяемых значений состояния через вычисление, и не все алгоритмы могут быть эффективно реализованы таким образом. Где требуется истинное изменяемое состояние, an STRef обычно является естественным выбором в непараллельной установке. Обратите внимание, что вы, вероятно, не будете использовать MVar или STM в непараллельной настройке - нет причин использовать их в этом случае, и они заставят тебя войти в IO монада, даже если вам это не нужно.

  3. Да, есть сценарии программирования, где либо 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 значения для изменяемых переменных, как правило, будет наиболее эффективным.