为什么我会想使用 Maybe 而不是 List?

Why would I ever want to use Maybe instead of a List?

鉴于 Maybe 类型与 null 和单例列表的集合同构,为什么有人会想要使用 Maybe 类型,而我只能使用列表来适应缺失?

因为可能的列表有无限多,而 Maybe 类型的可能值也有限。它完美地代表了一件事或没有任何其他可能性的事情。

因为如果您将列表与模式 [][x] 进行匹配,这不是详尽匹配,您会收到警告,迫使您添加另一个案例永远不会接到电话或忽略警告。

然而,将 MaybeNothingJust x 进行匹配是详尽无遗的。因此,如果您未能匹配其中一种情况,您只会收到警告。

如果您选择的类型只能代表您可能实际生成的值,您可以依靠非详尽性警告来告诉您代码中您忘记检查给定案例的错误。如果您选择更多 "permissive" 类型,您将始终需要考虑警告是代表实际错误还是代表不可能的情况。

你应该努力拥有准确的类型。 Maybe表示正好有一个值或者有none。许多命令式语言用值 null.

表示 "none" 的情况

如果您选择列表而不是 Maybe,您的所有函数都将面临获得包含多个成员的列表的可能性。可能它们中的许多只会为一个值定义,并且必须在模式匹配上失败。通过使用 Maybe,您可以完全避免 class 的运行时错误。

在现有(和正确)答案的基础上,我将提及一个基于 typeclass 的答案。

不同的类型传达不同的意图 - returning a Maybe a 表示计算可能会失败,而 [a] 可能不会-确定性(或者,更简单地说,多个可能的 return 值)。

这说明不同的类型具有不同的类型类实例 - 这些实例迎合了类型所传达的基本本质。以 Alternative 及其运算符 (<|>) 为例,它表示在给定参数之间组合(或选择)的含义。

  • Maybe a 合并可能失败的计算只意味着取第一个不是 Nothing
  • 的计算
  • [a] 组合两个计算,每个计算都有多个 return 值只是意味着将所有可能的值连接在一起。

然后,根据您的函数使用的类型,(<|>) 的行为会有所不同。当然,您可能会争辩说您不需要 (<|>) 或类似的东西,但是您会错过 Haskell 的主要优势之一:它有许多高级组合器库。

作为一般规则,我们希望我们的类型尽可能贴身和直观。这样,我们就不会与标准库作对,而且我们的代码更具可读性。

Lisp、Scheme、Python、Ruby、JavaScript 等,每一种都设法与一种类型相处,你可以在 Haskell 中表示大额类型。每个处理 JavaScript(或其他)值的函数都必须准备好接收数字、字符串、函数、文档对象模型的一部分等,并在遇到意外情况时抛出异常。使用像 Haskell 这样的类型化语言编程的人更喜欢限制可能发生的意外事件的数量。他们还喜欢使用类型来表达 想法 ,使类型成为有用的(和机器检查的)文档。类型越接近于表示预期的含义,它们就越有用。

这里有几个答案提到详尽无遗。我认为这是一个因素,但不是最大的因素,因为有一种方法可以始终如一地将列表视为 Maybes,listToMaybe 函数说明了这一点:

listToMaybe :: [a] -> Maybe a
listToMaybe [] = Nothing
listToMaybe (a:_) = Just a

这是一个详尽的模式匹配,排除了任何简单的错误。

我要强调的更大的因素是,通过使用更精确地模拟代码行为的类型,您可以消除使用更通用的替代方案时可能出现的潜在行为。例如,假设您在代码中有一些上下文,您在其中使用 a -> [b] 形式的类型,尽管唯一正确的替代方案(给定您的程序规范)是空列表或单例列表。尽可能努力地执行此上下文应遵守该规则的约定,您仍然有可能搞砸并且:

  1. 在该上下文中使用的函数会以某种方式生成包含两个或更多项的列表;
  2. 不知何故,使用在该上下文中产生的结果的函数将观察列表是否有两个或更多项目,并在这种情况下表现不正确。
    • 示例:某些期望值不超过一个的代码会盲目地打印列表的内容,从而在本应只有一个的情况下打印多个项目。

但是如果您使用 Maybe,那么实际上必须有一个值或 none,并且编译器会强制执行此操作。

即使是同构的,例如由于搜索 space 的增加,QuickCheck 将 运行 变慢。