为什么Maybe包括Just?

时间:2015-08-17 19:48:13

标签: haskell optional

感谢some excellent answers here,我通常理解(显然是有限的)Haskell Maybe的目的,其定义是

data Maybe a = Nothing | Just a

然而,我并非实体明确为什么Just是此定义的一部分。尽管我可以说,这是Just本身的定义,但相关文档并没有对此有太多的了解。

我是否正确认为在Just的定义中使用Maybe的主要好处,而不仅仅是

data Maybe a = Nothing | a

是否允许使用Just _进行模式匹配,以及isJustfromJust等有用的功能?

为什么Maybe以前者而不是后者定义?

4 个答案:

答案 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; StringString,&#34;因此,您可能会认为它与String相同。但它不是,因为Haskell联盟是标记的联盟,因此Either String String不仅记录了String,还记录了哪些&#34;标记& #34; (数据构造函数;在本例中为LeftRight)用于构造它。因此,即使两个备选方案都带有String作为其有效负载,您也可以告诉我们最初构建任何一个值的方式。这很好,因为很多情况下替代品是相同的类型,但构造函数/标签赋予了额外的含义:

data ResultMessage = FailureMessage String | SuccessMessage String

这里的数据构造函数是FailureMessageSuccessMessage,您可以从名称中猜测,即使两种情况下的有效负载都是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 NothingNothing之间的区别,因为不再有Just将它们分开。实际上,Maybe (Maybe a)会崩溃为Maybe a。这将是一个不方便的特殊情况。