Подтвердить что ты не робот

Следует ли мне избежать использования Monad?

Я новичок в Haskell и медленно понял, что что-то не так с существованием Monad терпит неудачу. Real World Haskell предупреждает об использовании ( "Еще раз рекомендуем вам всегда избегать использования сбоя!" ). Я только что заметил сегодня, что Росс Патерсон назвал его "бородавкой, а не образцом дизайна" еще в 2008 году (и, похоже, получил довольно определенное согласие в том, что нить).

Во время просмотра Dr Ralf Lämmel поговорите о сути функционального программирования, я начал понимать возможное напряжение, которое могло привести к тому, что Монада потерпит неудачу. В лекции Ральф говорит о добавлении различных монадических эффектов к базовому монадическому парсеру (каротаж, состояние и т.д.). Многие из эффектов потребовали изменения в базовом анализаторе, а иногда и в используемых типах данных. Я полагал, что добавление "неудачи" ко всем монадам могло быть компромиссом, потому что "провал" настолько распространен, и вы хотите как можно больше избегать изменений в "базовом" парсере (или любом другом). Конечно, какой-то "провал" имеет смысл для парсеров, но не всегда, скажем, put/get of state или ask/local Reader.

Сообщите мне, если я могу быть на неправильном пути.

Должен ли я избегать использования Monad? Каковы альтернативы Монаду? Существуют ли альтернативные библиотеки монад, которые не включают эту "бородавку"? Где я могу больше узнать об истории вокруг этого проектного решения?

4b9b3361

Ответ 1

Некоторые монады имеют разумный механизм отказа, например. терминальная монада:

data Fail x = Fail

Некоторые монады не имеют разумного механизма отказа (undefined неразумно), например. исходная монада:

data Return x = Return x

В этом смысле ясно, что бородавка требует, чтобы все монады имели метод fail. Если вы пишете программы, которые абстрагируются над монадами (Monad m) =>, не очень полезно использовать этот общий метод m fail. Это приведет к функции, которую вы можете создать с помощью монады, где fail не должно существовать действительно.

Я вижу меньше возражений против использования fail (особенно косвенно, путем сопоставления Pat <- computation) при работе в конкретной монаде, для которой четко указано хорошее поведение fail. Такие программы, мы надеемся, выживут в результате возвращения к старой дисциплине, где нетривиальное сопоставление шаблонов создало спрос на MonadZero вместо просто Monad.

Можно утверждать, что лучшая дисциплина всегда заключается в явном рассмотрении случаев отказа. Я возражаю против этой позиции по двум пунктам: (1), что точка монадического программирования состоит в том, чтобы избежать такого беспорядка, и (2), что текущая нотация для анализа случая на результате монадического вычисления настолько ужасна. Следующий выпуск SHE будет поддерживать обозначение (также найдено в других вариантах)

case <- computation of
  Pat_1 -> computation_1
  ...
  Pat_n -> computation_n

который может немного помочь.

Но вся эта ситуация - жалкая беспорядок. Часто полезно охарактеризовать монады с помощью операций, которые они поддерживают. Вы можете видеть fail, throw и т.д. Как операции, поддерживаемые некоторыми монадами, но не другие. Haskell делает его довольно неуклюжим и дорогостоящим для поддержки небольших локализованных изменений в множестве доступных операций, вводя новые операции, объясняя, как обращаться с ними по старым. Если мы серьезно хотим сделать более аккуратную работу здесь, нам нужно переосмыслить, как работает catch, чтобы сделать его переводчиком между различными локальными механизмами обработки ошибок. Я часто хочу скопировать вычисление, которое может завершиться неудачно (например, с ошибкой совпадения шаблонов) с обработчиком, который добавляет больше контекстуальной информации, прежде чем передавать ошибку. Я не могу не чувствовать, что иногда это бывает сложнее, чем должно быть.

Итак, это может быть лучше, но, по крайней мере, используйте fail только для определенных монадов, которые предлагают разумную реализацию и правильно обрабатывают "исключения".

Ответ 2

В Haskell 1.4 (1997) не было fail. Вместо этого существовал класс типа MonadZero, который содержал метод zero. Теперь в обозначении do используется zero, чтобы указать неудачу совпадения шаблонов; это вызвало удивление людей: нужна ли их функция Monad или MonadZero, как они использовали нотацию do в ней.

Когда Haskell 98 был разработан немного позже, они сделали несколько изменений, чтобы упростить программирование для новичков. Например, понимание монады превратилось в понимание списка. Аналогично, чтобы удалить проблему класса типа do, класс MonadZero был удален; для использования do метод fail был добавлен к Monad; и для других применений zero, mzero был добавлен метод MonadPlus.

Есть, я думаю, хороший аргумент в пользу того, что fail нельзя использовать для чего-либо явно; его единственное предназначение - в переводе обозначений do. Тем не менее, я сам часто непослушный и явно использую fail.

Вы можете получить доступ к исходным отчетам 1.4 и 98 здесь. Я уверен, что обсуждение, ведущее к изменению, можно найти в некоторых архивах списка адресов электронной почты, но у меня нет ссылок.

Ответ 3

Я стараюсь избегать Монады, когда это возможно, и есть множество способов захвата неудачи в зависимости от ваших обстоятельств. Эдвард Ян написал хороший обзор в своем блоге в статье под названием 8 способов сообщить об ошибках в Haskell.

Таким образом, различные способы сообщать об ошибках, которые он идентифицирует, следующие:

  • Использовать ошибку
  • Использовать Может быть
  • Использовать любую строку a
  • Использовать Monad и не обобщать 1-3
  • Использование MonadError и настраиваемый тип ошибки
  • Использовать бросок в монаде IO
  • Использовать ioError и catch
  • Пойдите гайками с монадными трансформаторами
  • Проверенные исключения
  • Отказ

Из них у меня возникнет соблазн использовать параметр 3 с Either e b, если я знаю, как обрабатывать эту ошибку, но нужно немного больше контекста.