7

Edx Haskell 课程中,Erik Meijer 反复声明Maybe不应该使用该类型进行失败的计算。相反,应该为此使用空列表。

我的理解是Maybe类型是个好东西,我们应该使用它。然而,一个列表似乎可以模拟所有的东西,一个Maybe罐头模型等等……那么为什么我们需要这个Maybe类型呢?

4

5 回答 5

17

然而,一个列表似乎可以对 Maybe 可以建模的所有内容进行建模,甚至更多

“和更多”是使用Maybe. 作为列表的使用者,您需要能够处理零个、一个或多个值。作为 a 的使用者Maybe,您只需要能够处理零个或一个值。因此,在多个值没有意义的情况下,最好使用它,Maybe这样您就可以静态地知道您不会得到无意义的值。

于 2014-11-24T10:38:43.290 回答
8

列表可以对任意数量的结果进行建模。另一方面,对一个结果或根本没有结果Maybe建模。

考虑以下功能:

f1 :: A -> [B]
f2 :: B -> [C]
f3 :: C -> [D]
f4 :: D -> [E]

目前尚不清楚有多少元素f1f2或返回。那么如果你对它们进行排序会发生什么?f3f4

f  :: A -> [E]
f s = f1 s >>= f2 >>= f3 >>= f4

结果应该包含多少个元素?一?零?我们是否不小心创建了一个包含 n^n(n ~ 输入长度)元素的列表?

但是,如果计算将只返回一个值或根本不返回值,则正确的类型会立即为我们提供所有必要的信息:

f1 :: A -> Maybe B
f2 :: B -> Maybe C
f3 :: C -> Maybe D
f4 :: D -> Maybe E

f  :: A -> Maybe E
f s = f1 s >>= f2 >>= f3 >>= f4

就是这样。现在回到 Meijer 的声明:

Erik Meijer 反复指出,不应该使用 Maybe 类型进行失败的计算。相反,应该为此使用空列表。

没有任何额外的客观推理,这只是个人喜好。我可以告诉每个人fmap都比map,这是我们应该做的事情。到那时,你要么相信我,要么问问题。如果他在演讲中没有说清楚,直接问他。

于 2014-11-24T10:49:27.303 回答
1

赞成名单:

  • 额外的值不是问题。当有多个结果时,客户端始终可以选择忽略列表的其余部分。

  • 当我们必须混合使用时,仅使用列表可以避免在 Maybe 和列表之间进行繁琐的转换。不需要listToMaybeor maybeToList

  • catMaybes变得只是concat(或join)。

一个可能的担忧是重复使用(>>=)for list monad 可以创建非常大的列表。然而,Haskell 很懒惰。如果我们只使用第一个元素,则不会计算列表的其余部分。

>>> head (let xs = [1..1000000] in xs >>= \_ -> xs >>= \_ -> xs)
1
于 2014-11-24T21:39:22.647 回答
1

我想我会在这里加入合唱团,说我无法评估 Meijer 的建议,除非向我展示了他的论点的所有细节。对我来说,这似乎很简单:

  1. 用于Maybe返回 0 或 1 结果的函数。
  2. 用于[]返回 0 个或多个结果的函数。
  3. 如果您需要在做出不同选择的函数之间进行混合和匹配,一种选择是使用 likelistToMaybe :: [a] -> Maybe amaybeToList :: Maybe a -> [a]fromData.Maybe函数来调整以一种样式编写的函数以在另一种样式中工作。
  4. 如果您想延迟选择是否使用Maybeor [],您可以使用AlternativeorMonadPlus类。

第 4 点的示例:

import Control.Applicative (pure, Alternative(..))

safeDiv :: (Alternative f, Fractional a, Eq a) => a -> a -> f a
safeDiv _ 0 = empty
safeDiv x y = pure (x / y)

{-

>>> safeDiv 5 2 :: Maybe Float
Just 2.5

>>> safeDiv 5 0 :: Maybe Float
Nothing

>>> safeDiv 5 2 :: [Float]
[2.5]

>>> safeDiv 5 0 :: [Float]
[]

-}

bothSqrt :: (Alternative f, Floating a) => a -> f a
bothSqrt x = let x' = sqrt x
             in pure x' <|> pure (-x')

{-

>>> bothSqrt 5 :: Maybe Float
Just 2.236068

>>> bothSqrt 5 :: [Float]
[2.236068,-2.236068]

>>> bothSqrt 5 >>= flip safeDiv 2 :: Maybe Float
Just 1.118034

>>>> bothSqrt 5 >>= flip safeDiv 2 :: [Float]
[1.118034,-1.118034]

-}
于 2014-11-26T02:10:26.430 回答
0

Maybe 的另一个有价值的点是它只是错误处理 monad 的最简单的例子,它可以用来以方便和一致的方式表示和组合“易碎”的计算(另一个例子是 Either 和纯异常)。列表单子在语义上是不同的(它表示非确定性计算),并且仅在空/单例情况下具有类似的行为,如上所示。

于 2014-11-24T13:44:47.330 回答