感谢some excellent answers here,我通常理解(显然是有限的)Haskell Maybe
的目的,其定义是
data Maybe a = Nothing | Just a
然而,我并非实体明确为什么Just
是此定义的一部分。尽管我可以说,这是Just
本身的定义,但相关文档并没有对此有太多的了解。
我是否正确认为在Just
的定义中使用Maybe
的主要好处,而不仅仅是
data Maybe a = Nothing | a
是否允许使用Just _
进行模式匹配,以及isJust
和fromJust
等有用的功能?
为什么Maybe
以前者而不是后者定义?
答案 0 :(得分:33)
Haskell的代数数据类型是标记的联合。按照设计,当你将两种不同的类型组合成另一种类型时,他们有来使构造函数消除歧义。
您的定义与代数数据类型的工作方式不符。
data Maybe a = Nothing | a
此处a
没有“标记”。在你的情况下,我们如何告诉Maybe a
除了正常的,未解开的a
?
Maybe
有一个Just
构造函数,因为 具有构造函数的设计。
其他语言确实有union types可以像您想象的那样工作,但它们不适合Haskell。它们在实践中表现不同,往往容易出错。
将标记联合作为普通联合类型的选择有一些强有力的设计理由。他们适用于类型推断。实际代码中的工会通常都有一个标签¹。而且,从优雅的角度来看,标记的联合对语言来说是“强烈的”自然契合,因为它们是产品(即元组和记录)的对偶。如果你很好奇,我在博文introducing and motivating algebraic data types中写了这篇文章。
<强>脚注强>
¹我在两个地方使用了联合类型:TypeScript和C.TypeScript编译为动态类型的JavaScript,这意味着它在运行时跟踪值的类型 - 基本上是标记。
C不是,但实际上,90%的联合类型用法都有标记或有效模拟结构子类型。我的一位教授实际上对如何在真正的C代码中使用了联合进行了实证研究,但我不记得它是什么样的纸张。
答案 1 :(得分: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
这允许您拥有以下值:
example1, example2 :: Either String Int
example1 = Left "Hello!"
example2 = Right 42
......但也喜欢这个:
example3, example4 :: Either String String
example3 = Left "Hello!"
example4 = Right "Hello!"
类型Either String String
,当您第一次遇到它时,听起来像是#34; String
或String
,&#34;因此,您可能会认为它与String
相同。但它不是,因为Haskell联盟是标记的联盟,因此Either String String
不仅记录了String
,还记录了哪些&#34;标记& #34; (数据构造函数;在本例中为Left
和Right
)用于构造它。因此,即使两个备选方案都带有String
作为其有效负载,您也可以告诉我们最初构建任何一个值的方式。这很好,因为很多情况下替代品是相同的类型,但构造函数/标签赋予了额外的含义:
data ResultMessage = FailureMessage String | SuccessMessage String
这里的数据构造函数是FailureMessage
和SuccessMessage
,您可以从名称中猜测,即使两种情况下的有效负载都是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
等类型。如果你考虑一下,如果我们的类型定义与你写的一样:
Maybe (Maybe a)
...我们想要创建example5, example6 :: Maybe (Maybe a)
example5 = Nothing
example6 = Just Nothing
类型的值,您将无法区分这两个值:
-- 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)
一开始看起来有点傻,但想象你有一张地图,其价值是&#34;可空的&#34;:
Map.lookup :: Ord k => Map k v -> k -> Maybe v
...想要查找条目:
mary
因此,如果我们在地图中查找Map.lookup favoriteNumber mary :: Maybe (Maybe Int)
,我们就会:
Nothing
现在结果Just Nothing
表示玛丽不在地图中,而import sys, psycopg2
with psycopg2.connect("dbname='XXXXX' user='YYYYY' password='ZZZZ'") as outdb:
cur. outdb.cursor()
cur.execute("CREATE TABLE primaryIds "
"( "
"City VARCHAR(20) NOT NULL, "
"Country VARCHAR(20) NOT NULL, "
"PRIMARY KEY (City, Country), "
"UNIQUE (City) "
"); ")
cur.execute("INSERT INTO primaryIds "
"(City, Country)"
" VALUES "
"(%s, %s)", (a, b))
cur.execute("CREATE TABLE trips "
"( "
"City VARCHAR(20) NOT NULL, "
"Country VARCHAR(20) NOT NULL, "
"Timestamp TIMESTAMP, "
"visitedDays INT, "
"Ids VARCHAR(20) REFERENCES primaryIds(City), "
"PRIMARY KEY (City, Country) "
"); ")
表示玛丽在地图中,但她没有最喜欢的号码。
答案 2 :(得分:7)
Just
是一个构造函数,当a
构造不同的类型a
时,Just a
只有Maybe a
类型。
答案 3 :(得分:3)
Maybe a
的设计目的是为了比a
类型多一个值。在类型理论中,有时它被写成1 + a
(最多为iso),这使得事实更加明显。
作为实验,请考虑类型Maybe (Maybe Bool)
。这里我们有1 + 1 + 2
个值,即:
Nothing
Just Nothing
Just (Just False)
Just (Just True)
如果我们被允许定义
data Maybe a = Nothing | a
我们会失去案例Just Nothing
和Nothing
之间的区别,因为不再有Just
将它们分开。实际上,Maybe (Maybe a)
会崩溃为Maybe a
。这将是一个不方便的特殊情况。