感谢这里的一些优秀答案,我通常理解(显然是以有限的方式)Haskell的目的,Maybe并且它的定义是
data Maybe a = Nothing | Just a
Run Code Online (Sandbox Code Playgroud)
但是,我并不清楚实体为什么Just是这个定义的一部分.尽管我已经知道了,但这是它Just自己定义的地方,但相关文档并没有多说.
我是否正确认为Just在定义中使用的主要好处Maybe,而不是简单
data Maybe a = Nothing | a
Run Code Online (Sandbox Code Playgroud)
它是否允许模式匹配Just _和有用的功能,如isJust和fromJust?
为什么Maybe以前一种方式而不是后一种方式定义?
Tik*_*vis 34
Haskell的代数数据类型是带标记的联合.按照设计,当您将两种不同类型组合成另一种类型时,它们必须具有构造函数来消除它们的歧义.
您的定义与代数数据类型的工作方式不相符.
data Maybe a = Nothing | a
Run Code Online (Sandbox Code Playgroud)
这里没有"标签" a.在你的情况下Maybe a,我们如何区分正常的,未解开a的?
Maybe有一个Just构造函数,因为它必须有一个设计的构造函数.
其他语言的联合类型可以像你想象的那样工作,但它们不适合Haskell.它们在实践中表现不同,往往容易出错.
将标记联合作为普通联合类型的选择有一些强有力的设计理由.他们适用于类型推断.实际代码中的工会通常都有一个标签¹.而且,从优雅的角度来看,带标签的联合会很自然地适合语言,因为它们是产品的双重性(即元组和记录).如果你很好奇,我在一篇介绍和激励代数数据类型的博客文章中写到了这一点.
脚注
¹我在两个地方使用了联合类型:TypeScript和C.TypeScript编译为动态类型的JavaScript,这意味着它在运行时跟踪值的类型 - 基本上是标记.
C不是,但实际上,90%的联合类型使用都有标记或有效模拟结构子类型.我的一位教授实际上对如何在真正的C代码中使用了联合进行了实证研究,但我不记得它是什么样的纸张.
Lui*_*las 17
查看它的另一种方法(除了Tikhon的答案)是考虑另一种基本的Haskell类型Either,它的定义如下:
-- | A value that contains either an @a@ (the 'Left') constructor) or
-- a @b@ (the 'Right' constructor).
data Either a b = Left a | Right b
Run Code Online (Sandbox Code Playgroud)
这允许您具有以下值:
example1, example2 :: Either String Int
example1 = Left "Hello!"
example2 = Right 42
Run Code Online (Sandbox Code Playgroud)
......还喜欢这个:
example3, example4 :: Either String String
example3 = Left "Hello!"
example4 = Right "Hello!"
Run Code Online (Sandbox Code Playgroud)
这种类型Either String String,当你第一次遇到它时,听起来像"a String或a String",你可能因此认为它和它一样String.但事实并非如此,因为Haskell联盟是标记的联合,因此Either String String记录不仅仅是a String,而且还使用了哪些"标签"(数据构造函数;在这种情况下)Left和Right它来构造它.因此,即使两个备选方案都带有一个String有效载荷,您也可以告诉我们最初构建的任何一个值.这很好,因为有很多情况下,替代品是相同的类型,但构造函数/标签赋予额外的意义:
data ResultMessage = FailureMessage String | SuccessMessage String
Run Code Online (Sandbox Code Playgroud)
这里的数据构造函数是FailureMessage和SuccessMessage,您可以从名称中猜测,即使两种情况下的有效负载都是a String,它们也意味着非常不同的东西!
所以把它带回Maybe/ Just,这里发生的事情是Haskell只是统一地工作:联合类型的每个替代都有一个独特的数据构造函数,必须始终用于构造和模式匹配其类型的值.即使一开始您可能认为可以从上下文中猜测它,但它不会这样做.
还有其他原因,有点技术性.首先,惰性评估的规则是根据数据构造函数定义的.简短的版本:懒惰的评估意味着如果Haskell被迫在类型的值内部查看Maybe a,它将尝试进行所需的最少工作量以确定它是否看起来Nothing像是Just x- 优选它不会在内部窥视在x当它这样做.
第二:语言需要能够区分类型Maybe a,Maybe (Maybe a)和Maybe (Maybe (Maybe a)).如果你考虑一下,如果我们有一个像你写的那样工作的类型定义:
data Maybe a = Nothing | a -- NOT VALID HASKELL
Run Code Online (Sandbox Code Playgroud)
...我们想要创建一个类型的值Maybe (Maybe a),你将无法区分这两个值:
example5, example6 :: Maybe (Maybe a)
example5 = Nothing
example6 = Just Nothing
Run Code Online (Sandbox Code Playgroud)
这一开始看起来有点傻,但想象一下你的地图的值是"可空的":
-- Map of persons to their favorite number. If we know that some person
-- doesn't have a favorite number, we store `Nothing` as the value for
-- that person.
favoriteNumber :: Map Person (Maybe Int)
Run Code Online (Sandbox Code Playgroud)
...并且想要查找条目:
Map.lookup :: Ord k => Map k v -> k -> Maybe v
Run Code Online (Sandbox Code Playgroud)
因此,如果我们mary在地图中查找,我们有:
Map.lookup favoriteNumber mary :: Maybe (Maybe Int)
Run Code Online (Sandbox Code Playgroud)
现在结果Nothing意味着Mary不在地图中,而在地图中Just Nothing意味着Mary,但她没有喜欢的数字.
Maybe a被设计为比 type 多一个值a。在类型论中,有时它被写为1 + a(直到 iso),这使得这个事实更加明显。
作为一个实验,考虑类型Maybe (Maybe Bool)。这里我们有1 + 1 + 2价值观,即:
Nothing
Just Nothing
Just (Just False)
Just (Just True)
Run Code Online (Sandbox Code Playgroud)
如果我们被允许定义
data Maybe a = Nothing | a
Run Code Online (Sandbox Code Playgroud)
我们将失去案例Just Nothing和 之间的区别Nothing,因为不再能够Just将它们分开。确实,Maybe (Maybe a)会崩溃成Maybe a。这将是一个不方便的特殊情况。