实际上它只是一个普通的数据构造函数,恰好定义在Prelude中,这是一个自动导入到每个模块的标准库。
Maybe的结构
定义看起来像这样:
data Maybe a = Just a
| Nothing
这个声明定义了一个类型 Maybe a
,它是由类型变量 a
参数化的,这意味着你可以在 a
的位置上使用任何类型。
构造和解构
这个类型有两个构造器: Just a
和 Nothing
。当一个类型有多个构造器时,这意味着该类型的值必须仅使用其中一个可能的构造器之一来构造。对于这个类型,一个值只能通过 Just
或 Nothing
构造,没有其他(非错误)可能性。
因为 Nothing
没有参数类型,所以当它被用作构造器时,它命名了一个常量值,该值是所有类型 a
的 Maybe a
类型的成员。但是 Just
构造器具有类型参数,这意味着当它被用作构造器时,它就像是从类型 a
到 Maybe a
的函数,即它具有类型 a -> Maybe a
。
因此,类型的构造器用于构建该类型的值;另一方面,当你想要使用该值时,模式匹配就发挥作用了。与函数不同,构造器可以用于模式绑定表达式,这是你可以对属于具有多个构造器的类型的值进行 case 分析 的方式。
为了在模式匹配中使用 Maybe a
值,需要为每个构造器提供一个模式,如下所示:
case maybeVal of
Nothing -> "There is nothing!"
Just val -> "There is a value, and it is " ++ (show val)
在这种情况下,如果值是Nothing
,第一个模式将匹配,如果该值是用Just
构造的,则第二个模式将匹配。如果第二个模式匹配,则还会将名称val
绑定到传递给Just
构造函数的参数,当构造要匹配的值时。
Maybe代表什么
也许您已经熟悉这个操作方式; Maybe
值没有真正的魔力,它只是一种常规的Haskell代数数据类型(ADT)。但是由于它有效地将类型(例如来自您示例中的Integer
)“提升”或扩展到具有表示缺少值的额外值(Nothing
)的新上下文中!然后,类型系统要求您在获取可能存在的Integer
之前检查该额外值。这可以防止大量的错误。
今天许多语言通过NULL引用处理此类“无值”值。著名计算机科学家Tony Hoare(他发明了Quicksort并获得了图灵奖)承认这是他的“亿万美元的错误”。Maybe类型不是唯一修复此错误的方法,但已被证明是一种有效的方法。
Maybe作为Functor
将一种类型转换为另一种类型,使得对旧类型的操作也可以转换为适用于新类型的操作,这就是Haskell类型类Functor
背后的概念,其中Maybe a
具有一个有用的实例。
Functor
提供了一个称为fmap
的方法,该方法将基类型(例如Integer
)上的取值范围映射到提升类型(例如Maybe Integer
)上的取值范围。使用fmap
转换为在Maybe
值上工作的函数的函数像这样工作:
case maybeVal of
Nothing -> Nothing -- there is nothing, so just return Nothing
Just val -> Just (f val) -- there is a value, so apply the function to it
如果您有一个
Maybe Integer
值
m_x
和一个
Int -> Int
函数
f
,则可以执行
fmap f m_x
,直接将函数
f
应用于
Maybe Integer
,而不必担心它是否实际上具有值。实际上,您可以将整个提升的
Integer -> Integer
函数链应用于
Maybe Integer
值,只需要在完成后明确检查
Nothing
一次即可。
也许作为单子
我不确定您对单子概念有多熟悉,但您至少已经使用过
IO a
,并且类型签名
IO a
看起来非常类似于
Maybe a
。尽管
IO
是特殊的,因为它不向您公开其构造函数,并且因此只能由Haskell运行时系统“运行”,但它还是一个
Functor
,除了是一个
Monad
。实际上,单子就是一种特殊类型的
Functor
,具有一些额外的功能,但这不是讨论的地方。
无论如何,像
IO
这样的单子将类型映射到表示“产生值的计算”的新类型,您可以通过一个非常类似于
fmap
的函数
liftM
将函数提升到
Monad
类型中,该函数将普通函数转换为“计算结果是通过评估函数获得的值的计算”。
您可能已经猜到(如果您读到这里),
Maybe
也是
Monad
。它表示“可能无法返回值的计算”。就像使用
fmap
示例一样,这使您能够执行整个计算过程,而无需在每个步骤之后明确检查错误。实际上,
Monad
实例的构造方式是,当遇到
Nothing
时,
Maybe
值上的计算会立即停止,因此它有点像中途的立即中止或无值返回的计算。
您本来可以编写Maybe
正如我之前所说,与语言语法或运行时系统无关的
Maybe
类型固有的内容没有任何东西。如果Haskell没有默认提供它,则可以自己提供其所有功能!实际上,您仍然可以使用不同的名称再次编写它,并获得相同的功能。
希望您现在了解
Maybe
类型及其构造函数,但如果还有任何不清楚的地方,请告诉我!
Maybe
,而其他编程语言可能会使用null
或nil
(带有令人讨厌的NullPointerException
潜伏在每个角落)。现在其他编程语言也开始使用这种构造:Scala 使用Option
,甚至 Java 8 也引入了Optional
类型。 - Landeidata Maybe a = Nothing | a
呢?那样行不行? - Richard