我正在玩弄Maybe和Either单子类型(根据返回值链接、应用条件函数,还可以返回链式函数失败的错误消息等)。因此,看起来我们可以通过使用Either单子实现与Maybe相同甚至更多的事情。所以我的问题是,这两者之间的实际或概念差异在哪里?
我正在玩弄Maybe和Either单子类型(根据返回值链接、应用条件函数,还可以返回链式函数失败的错误消息等)。因此,看起来我们可以通过使用Either单子实现与Maybe相同甚至更多的事情。所以我的问题是,这两者之间的实际或概念差异在哪里?
你当然是正确的,Maybe a
和 Either Unit a
是同构的。问题在于它们通常用于表示不同的语义,有点类似于返回null
和抛出NoSuchElementException
之间的区别:
Nothing
/ None
表示预期缺少某些东西,而Left e
表示由于任何原因获取它时出现错误。话虽如此,我们甚至可以将两者结合起来得到:
query :: Either DBError (Maybe String)
在一些情况下,我们需要表达可能缺失的值(例如数据库中的 NULL
)以及连接错误、DBMS 或其他问题(并不是说这是最好的设计,但你可以理解这一点)。
有时边界是模糊的;对于 saveHead :: [a] -> Maybe a
,我们可以说函数的意图中编码了错误的可能性,而像 saveDivide
这样的函数则可以根据用例编码为 Float -> Float -> Either FPError Float
或 Float -> Float -> Maybe Float
(再次强调,这只是愚蠢的示例...)。
如果有疑问,最好的选择可能是使用自定义结果 ADT 进行语义编码(例如 data QueryResult = Success String | Null | Failure DBError
),并在“传统期望”使用 Maybe
的简单情况下优先考虑使用它(这是一个主观的观点,但如果你积累了足够的经验,它基本上是可以的)。
@phg的回答很好。我想加入一些有助于理解它们的内容:
Maybe
代表一个(值)或无(值)——即,你要么有一个值,要么什么都没有Either
是一个逻辑上的析取,但你始终至少有一个(值)——即,你有其中之一,但不是两者都有。Maybe
非常适用于可能有也可能没有值的情况——例如在列表中查找项。如果列表包含它,我们会得到(Just x)
,否则我们会得到Nothing
Either
是代码分支的完美表示方式——它将走向两个方向之一;Left
或Right
。我们使用助记词来记住它:Right
是正确的路线;Left
是错误的路线(出现错误)。当然,这不是它唯一的用途,但绝对是最常见的。
起初,这些区别可能似乎微妙,但它们实际上非常适合不同的情况。
你看,我们可以通过说所有产品类型都可以用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