为什么我们需要 Maybe Monad 而不是 Either Monad
Why Do We Need Maybe Monad Over Either Monad
我在玩 Maybe 和 Either monad 类型(链接,根据返回值应用条件函数,还返回链接函数失败的错误消息等)。所以在我看来,我们可以通过使用 Either monad 来实现 Maybe 所做的相同和更多的事情。所以我的问题是它们之间的实际或概念差异在哪里?
Maybe a
与 Either Unit a
同构,你当然是对的。问题是它们通常在语义上用于表示不同的事物,有点像返回 null
和抛出 NoSuchElementException
之间的区别:
Nothing
/None
表示"expected"缺少某物,而
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
而不是 "traditionally expected" 的简单情况](一个主观点,但是如果您获得经验,这将是基本可以的)。
@phg 的回答很棒。当我学习它们时,我会插话帮助我理清头绪:
Maybe
是一个(值)或 none – 即,你有一个值,或者你什么都没有
Either
是一个逻辑析取,但您始终至少有一个(值)- 即,您有一个 或 另一个,但不能同时有两个。
Maybe
非常适合您可能有或没有价值的事情 - 例如在列表中查找项目。如果列表包含它,我们得到 (Maybe x)
否则我们得到 Nothing
Either
是您代码中分支的完美表示 - 它会以一种方式或另一种方式进行; Left
或 Right
。我们用一个助记符来记:Right
就是对(正确)的方式; Left
是 错误的 方式(错误)。这当然不是唯一的用途,但绝对是最常见的。
我知道这些差异起初可能看起来很微妙,但实际上它们适用于非常不同的事物。
好吧,你看,我们可以说所有产品类型都可以只用 2 元组表示,所有非递归和类型都可以用 Either
来表示。为了额外表示递归类型,我们需要一个定点类型。
例如,当我们也可以写 (a, (b, (c,d)))
或 (((a,b), c), d)
时,为什么要有 4 元组 (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
我在玩 Maybe 和 Either monad 类型(链接,根据返回值应用条件函数,还返回链接函数失败的错误消息等)。所以在我看来,我们可以通过使用 Either monad 来实现 Maybe 所做的相同和更多的事情。所以我的问题是它们之间的实际或概念差异在哪里?
Maybe a
与 Either Unit a
同构,你当然是对的。问题是它们通常在语义上用于表示不同的事物,有点像返回 null
和抛出 NoSuchElementException
之间的区别:
Nothing
/None
表示"expected"缺少某物,而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
而不是 "traditionally expected" 的简单情况](一个主观点,但是如果您获得经验,这将是基本可以的)。
@phg 的回答很棒。当我学习它们时,我会插话帮助我理清头绪:
Maybe
是一个(值)或 none – 即,你有一个值,或者你什么都没有Either
是一个逻辑析取,但您始终至少有一个(值)- 即,您有一个 或 另一个,但不能同时有两个。
Maybe
非常适合您可能有或没有价值的事情 - 例如在列表中查找项目。如果列表包含它,我们得到 (Maybe x)
否则我们得到 Nothing
Either
是您代码中分支的完美表示 - 它会以一种方式或另一种方式进行; Left
或 Right
。我们用一个助记符来记:Right
就是对(正确)的方式; Left
是 错误的 方式(错误)。这当然不是唯一的用途,但绝对是最常见的。
我知道这些差异起初可能看起来很微妙,但实际上它们适用于非常不同的事物。
好吧,你看,我们可以说所有产品类型都可以只用 2 元组表示,所有非递归和类型都可以用 Either
来表示。为了额外表示递归类型,我们需要一个定点类型。
例如,当我们也可以写 (a, (b, (c,d)))
或 (((a,b), c), d)
时,为什么要有 4 元组 (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