Я играл вокруг Maybe и Либо типов монады (Chaining, применяя условные функции в соответствии с возвращенным значением, также возвращал сообщение об ошибке, вызванное функцией цепочки и т.д.). Поэтому мне кажется, что мы можем добиться того же и большего количества вещей, которые, возможно, делают, используя любую монаду. Итак, мой вопрос заключается в том, где практическая или концептуальная разница между ними?
Зачем нам нужно быть Монадой над Либо Монадой
Ответ 1
Вы, конечно, правы, что Maybe a
изоморфен Either Unit a
. Дело в том, что они часто семантически используются для обозначения разных вещей, что немного напоминает разницу между возвращением null
и бросанием NoSuchElementException
:
-
Nothing
/None
обозначает "ожидаемое" отсутствие чего-либо, а -
Left e
означает ошибку при получении по любой причине.
Тем не менее, мы могли бы даже совместить эти два с чем-то вроде:
query :: Either DBError (Maybe String)
где мы выражаем как возможность отсутствующего значения (DB null
), так и ошибку в соединении, СУБД и т.д. (не говоря о том, что нет улучшенных конструкций, но вы получаете точку).
Иногда граница является жидкой; для saveHead :: [a] -> Maybe a
можно сказать, что ожидаемая вероятность ошибки кодируется в намерении функции, а что-то вроде saveDivide
может быть закодировано как Float -> Float -> Either FPError Float
или Float -> Float -> Maybe Float
, в зависимости от варианта использования (опять же, просто некоторые глупые примеры...).
Если у вас есть сомнения, лучшим вариантом является, вероятно, использование пользовательского результата ADT с семантическим кодированием (например, data QueryResult = Success String | Null | Failure DBError
) и предпочесть Maybe
на простые случаи, когда он "традиционно ожидается" (субъективная точка, которая однако будет в основном ОК, если вы приобретете опыт).
Ответ 2
@phg ответ велик. Я буду возиться с чем-то, что помогло мне разобраться в этом, когда я их изучил:
-
Maybe
- одно (значение) или none - то есть у вас есть значение или у вас ничего нет -
Either
- логическая дизъюнкция, но у вас всегда есть хотя бы одно (значение), то есть у вас есть один или другой, но не оба.
Maybe
отлично подходит для таких вещей, как то, где вы можете иметь или не иметь значения - например, поиск элемента в списке. если список содержит его, мы получаем (Maybe x)
, иначе получим Nothing
Either
- идеальное представление ветки в вашем коде - это будет идти так или иначе; Left
или Right
. Мы используем мнемонику, чтобы ее запомнить: Right
- правильный (правильный) способ; Left
- неправильный путь (ошибка). Это не только использование, но, безусловно, наиболее распространенное.
Я знаю, что вначале различия могут казаться утонченными, но на самом деле они подходят для самых разных вещей.
Ответ 3
Ну, вы видите, мы можем сказать это предельно, говоря, что все типы продуктов могут быть представлены только 2-мя корнями и всеми нерекурсивными типами сумм на Either
. Чтобы дополнительно представить рекурсивные типы, нам нужен тип фиксированной точки.
Например, почему есть 4-кортежи (a,b,c,d)
, когда мы могли бы написать (a, (b, (c,d)))
или (((a,b), c), d)
?
Или почему есть списки, когда работает следующее?
data Y f = Y (f (Y f))
type List a = Y ((,) (Either () a))
nil = Y (Left (), undefined)
cons a as = Y (Right a, as)
infixr 4 cons
numbers = 1 `cons` 2 `cons` 3 `cons` nil
-- this is like foldl
reduce f z (Y (Left (), _)) = z
reduce f z (Y (Right x, xs)) = reduce f (f z x) xs
total = reduce (+) 0 numbers