Каков идиоматический тип для представления нескольких случаев сбоя, но только одного случая успеха? - PullRequest
0 голосов
/ 27 апреля 2018

Тип Maybe a представляет вычисление, которое может потерпеть неудачу, с семантикой, которая нас совершенно не заботит, точно как потерпел неудачу. Если вычисление выполнено успешно, может быть возвращено любое значение типа a.

А как насчет обратного случая, когда вычисление может завершиться неудачей по ряду причин (и мы хотим сохранить эту информацию), но успех не включает в себя какую-либо информацию, кроме «да, это удалось»? Я могу придумать два очевидных способа кодирования такого рода вычислений:

  • Maybe e, где Just e обозначает сбой, а Nothing обозначает успех. Это настолько противоречит обычному использованию Maybe, что я не хотел бы его использовать.
  • Either e (), где Left e обозначает сбой, а Right () обозначает успех. Это имеет то преимущество, что является явным, но имеет тот недостаток, что ... является явным. Написание () чувствует себя неловко, особенно вне контекста сигнатуры типа.

Есть ли у Haskell более идиоматический способ представления «нескольких случаев неудач, но только одного случая успеха»?

Ответы [ 3 ]

0 голосов
/ 28 апреля 2018

Непонятно, как может произойти сбой в вычислениях.

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

type MyResult a = Either [Error] a

, который либо преуспевает с результатом, либо проваливается со списком причин.

С другой стороны, если у вас есть недетерминированные вычисления, где каждая вариация может быть успешной или неудачной, вам нужно что-то более похожее на:

type MyResult a = [Either Error a]

Затем выполните поиск в списке результатов. Если вы найдете Право, верните его, в противном случае соберите список Левых.

0 голосов
/ 28 апреля 2018

Коротким путем будет использование Either e () вместе с синонимом шаблона

pattern Success :: Either e ()  -- Explicit type signature not necessary
pattern Success = Right ()

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

type FailableWith e = Either e ()

pattern FailedWith :: e -> FailableWith e
pattern FailedWith x = Left x

В отличие от Maybe, Either обладает тем преимуществом, что все существующие механизмы уже установлены: Functor, Applicative, Monad, Foldable, Traversable, Semigroup и даже Ord (Left x < Right y всегда должен содержать) экземпляры, скорее всего, будут вести себя именно так, как вы хотели бы для обработки ошибок. Как правило, для этой конкретной ситуации Maybe будет иметь тенденцию делать противоположное тому, что вы хотите (обычно вы хотите продолжить успех и остановиться после первого сбоя, что противоположно тому, что будет делать большинство машин Maybe). обеспечить для этого сценария).

0 голосов
/ 28 апреля 2018

Не видя реального кода, на самом деле трудно понять, что вы подразумеваете под неудачей. Если это чистая функция, то я не вижу, что использование Maybe было бы проблемой. Я никогда не вижу Nothing неудачей, но такой, какой она есть: ничего. В зависимости от контекста я либо возвращаю Nothing, либо использую значение по умолчанию и продолжаю. Я понимаю, что это можно рассматривать как провал, но это больше зависит от точки зрения если вызывающая сторона, чем сама функция. Теперь вы хотите представить вычисление, которое может дать сбой, но ничего не возвращает. Если это чистая функция, это не имеет смысла. Вы работаете, будучи чистым, ничего не произошло (без побочных эффектов), и вы не получите результат. Так что в случае успеха вы фактически ничего не вычислили: это не успех, это ничего. ATHI Если вы терпите неудачу, у вас есть причина, почему это не удалось. Это ничем не отличается от простой проверки, возвращающей Maybe.

Например, вам может потребоваться проверить, что домен не находится в черном списке. Для этого вы выполняете поиск в списке. Ничто не означает, что все в порядке, хотя это означает, что это с вашей точки зрения и неудачи, и вам нужно остановить вычисления. Этот же код можно использовать для проверки того, что ваш домен входит в белый список. в этом случае ничто не является провалом: зависит только от контекста.

Теперь, если вы выполняете монадическое действие (например, сохранение файла или чего-то еще), имеет смысл ничего не возвращать, но могут произойти другие сбои (заполнение диска, неверный путь и т. Д.). Стандартная сигнатура для ввода-вывода, для которой мы не заботимся о результате, равна IO (), так что вы можете либо выбрать IO (Either e ()) (все это поймут), либо перейти на IO () и вызвать исключение (если они действительно исключительные) ).

...