为什么Maybe包括Just?

oro*_*ome 23 haskell optional

感谢这里的一些优秀答案,我通常理解(显然是以有限的方式)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 _和有用的功能,如isJustfromJust

为什么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代码中使用了联合进行了实证研究,但我不记得它是什么样的纸张.

  • 假设你有一个类型为`Maybe Maybe String`的变量,其值为'Nothing`.内在的可能没什么,或外在的? (4认同)
  • @raxacoricofallapatorius:对.这是一个有意识的设计决策,有一些强大的结果.除了模式匹配之外,它还有助于类型推断等. (3认同)

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,而且还使用了哪些"标签"(数据构造函数;在这种情况下)LeftRight它来构造它.因此,即使两个备选方案都带有一个String有效载荷,您也可以告诉我们最初构建的任何一个值.这很好,因为有很多情况下,替代品是相同的类型,但构造函数/标签赋予额外的意义:

data ResultMessage = FailureMessage String | SuccessMessage String
Run Code Online (Sandbox Code Playgroud)

这里的数据构造函数是FailureMessageSuccessMessage,您可以从名称中猜测,即使两种情况下的有效负载都是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,但她没有喜欢的数字.


w.b*_*w.b 7

Just当构造不同的类型时,它是一个构造函数, a单独是类型.aJust aMaybe a


chi*_*chi 5

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。这将是一个不方便的特殊情况。