为什么我们需要 Maybe Monad 而不是 Either Monad?

25

我正在玩弄Maybe和Either单子类型(根据返回值链接、应用条件函数,还可以返回链式函数失败的错误消息等)。因此,看起来我们可以通过使用Either单子实现与Maybe相同甚至更多的事情。所以我的问题是,这两者之间的实际或概念差异在哪里?


2
我很惊讶这不是重复的。好问题。 - Jared Smith
3个回答

24

你当然是正确的,Maybe aEither Unit a 是同构的。问题在于它们通常用于表示不同的语义,有点类似于返回null和抛出NoSuchElementException之间的区别:

  • Nothing / None表示预期缺少某些东西,而
  • Left e表示由于任何原因获取它时出现错误。

话虽如此,我们甚至可以将两者结合起来得到:

query :: Either DBError (Maybe String)

在一些情况下,我们需要表达可能缺失的值(例如数据库中的 NULL)以及连接错误、DBMS 或其他问题(并不是说这是最好的设计,但你可以理解这一点)。

有时边界是模糊的;对于 saveHead :: [a] -> Maybe a,我们可以说函数的意图中编码了错误的可能性,而像 saveDivide 这样的函数则可以根据用例编码为 Float -> Float -> Either FPError FloatFloat -> Float -> Maybe Float(再次强调,这只是愚蠢的示例...)。

如果有疑问,最好的选择可能是使用自定义结果 ADT 进行语义编码(例如 data QueryResult = Success String | Null | Failure DBError),并在“传统期望”使用 Maybe 的简单情况下优先考虑使用它(这是一个主观的观点,但如果你积累了足够的经验,它基本上是可以的)。


17

@phg的回答很好。我想加入一些有助于理解它们的内容:

  • Maybe代表一个(值)或无(值)——即,你要么有一个值,要么什么都没有
  • Either是一个逻辑上的析取,但你始终至少有一个(值)——即,你有其中之一,但不是两者都有。

Maybe非常适用于可能有也可能没有值的情况——例如在列表中查找项。如果列表包含它,我们会得到(Just x),否则我们会得到Nothing

Either是代码分支的完美表示方式——它将走向两个方向之一;LeftRight。我们使用助记词来记住它:Right正确的路线;Left错误的路线(出现错误)。当然,这不是它唯一的用途,但绝对是最常见的。

起初,这些区别可能似乎微妙,但它们实际上非常适合不同的情况。


1
提到逻辑或运算,您会得到+1。对于刚接触FP的人来说,这是非常易懂的答案。 - Jared Smith
谢谢Jared。你的反馈对我来说意义重大 ^_^ - Mulan
例如在列表中查找项目,我认为这个例子使理解差异变得更加困难了?这仍然看起来像Either。你要么得到一个元素,要么什么都没有。 - Farid
@Farid 不是,Either 表示“要么值 A,要么值 B”。Maybe 表示“可能有一个值,也可能没有值”。 - Mulan

1

你看,我们可以通过说所有产品类型都可以用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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接