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

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

  3. Существуют ли сценарии программирования, которые легко обрабатываются с использованием IORef вместо STM, MVar или чистого ввода-вывода?

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

11
user5803465 23 Сен 2018 в 19:13

1 ответ

Лучший ответ

Во-первых, я считаю, что для фрагментов кода в статье использование IORef совершенно разумно, особенно если статья не о лучших практиках для изменяемых ссылок или параллелизма. IORef прост для понимания, имеет простой синтаксис и семантику (особенно в непараллельной среде) и является естественным выбором, если вы хотите, чтобы читатель сосредоточился на аспектах ваших примеров, отличных от IORef с. К сожалению, подход автора имел неприятные последствия для вас - просто игнорируйте IORef и обращайте внимание на остальную часть того, о чем говорится в статье.

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

В любом случае, что касается вашего более крупного вопроса, основными возражениями против использования IORef будут:

  • Как и любой другой механизм для введения изменяемого состояния в вашу программу, он делает ваш код более сложным для размышлений, поддержки, тестирования и т. Д. - все обычные вещи, которые, по словам сторонников функционального программирования, дают FP "преимущество" над интенсивным изменением. императивные алгоритмы.
  • Это просто оболочка newtype вокруг специализированного STRef RealWorld, и единственное, что она добавляет поверх STRef, - это некоторые атомарные операции. В непараллельном коде нет веских причин не использовать значения STRef s в монаде ST s, поскольку они более гибкие - вы можете запускать их в чистом коде с помощью runST или , если необходимо, в монаде ввода-вывода с помощью stToIO.
  • В параллельном коде есть более мощные абстракции, такие как MVar и STM, с которыми намного проще работать, чем с IORef.

Таким образом, в той степени, в которой изменяемое состояние является «плохим» и - если оно вам действительно нужно - доступны лучшие альтернативы в зависимости от того, нужен ли вам параллелизм или нет, рекомендовать IORef особо нечего.

С другой стороны, если вы уже работаете над некоторым непараллельным кодом в монаде IO, потому что вам нужно выполнять фактические операции ввода-вывода, и вам действительно нужно какое-то повсеместное изменяемое состояние, которое не Трудно отделиться от ввода-вывода, тогда использование IORef кажется законным.

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

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

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

  3. Да, есть сценарии программирования, в которых IORef или STRef предпочтительнее, чем State, STM, MVar или чистый IO (см. Ниже ). Есть несколько сценариев, в которых IORef явно предпочтительнее, чем STRef, но - как упоминалось выше - если вы уже находитесь в монаде IO и вам нужно истинное изменяемое состояние, которое запутанный с операциями ввода-вывода, то IORef, вероятно, имеет преимущество перед STRef с точки зрения немного более чистого синтаксиса.

Некоторые примеры случаев, когда либо IORef, либо STRef является хорошим подходом:

  • Data.Unique в пакете base использует IORef в качестве глобального счетчика для создания уникальных объектов.
  • В библиотеке base внутренние дескрипторы файлов широко используют IORef для присоединения буферов к дескрипторам. Это хороший пример «уже находясь в монаде ввода-вывода с запутанными операциями ввода-вывода».
  • Многие векторные алгоритмы наиболее эффективно реализуются с использованием изменяемых векторов (например, даже таких простых вещей, как подсчет частот байтов в блоке данных). Если вы используете изменяемые векторы из пакета vector, то технически вы используете изменяемые байтовые массивы, а не STRef или IORef, но это все равно морально эквивалентно.
  • Пакет equivalence использует STRef для эффективной реализации алгоритма объединения-объединения.
  • В качестве наглядного примера: если вы реализуете интерпретатор для императивного языка, то использование значений IORef или STRef для изменяемых переменных обычно будет наиболее эффективным.
11
K. A. Buhr 23 Сен 2018 в 20:12