由于这里有一些很好的答案,我基本上(显然是以有限的方式)理解了Haskell的Maybe
的目的,并且它的定义是
data Maybe a = Nothing | Just a
然而,我不太清楚为什么Just
是这个定义的一部分。据我所知,这就是Just
本身的定义所在,但相关的留档并没有对此做太多说明。
我认为在的定义中使用
的主要好处可能是
,而不是简单地
data Maybe a = Nothing | a
它允许模式匹配到
Just
,并提供有用的功能,如isJust
和fromJust
?
为什么
也许
以前一种方式定义而不是后者?
Just
是一个构造函数,a
单独就属于a
类型,而Just a
构造了另一个类型可能是a
。
另一种看待它的方式(除了Tikhon的答案之外)是考虑另一种基本的Haskell类型,other
,其定义如下:
-- | 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!"
第一次遇到的类型是String
,听起来像“要么是String
,要么是String
,”因此,你可能会认为它与String
是一样的。但事实并非如此,因为Haskell联合是带标签的联合,因此任一字符串
不仅记录了字符串
,还记录了使用哪个“标记”(数据构造函数;在本例中是左
和右
)来构造它。因此,即使两个备选方案都携带一个字符串作为有效载荷,您也能够知道任何一个值最初是如何构建的。这很好,因为在很多情况下,备选方案都是相同的类型,但构造函数/标记赋予了额外的含义:
data ResultMessage = FailureMessage String | SuccessMessage String
这里的数据构造器是
FailureMessage
和SuccessMessage
,您可以从名称中猜到,尽管这两种情况下的有效负载都是字符串,但它们的含义却截然不同!
所以把它带回
也许
/只是
,这里发生的事情是Haskell就是这样统一工作的:联合类型的每个替代方案都有一个不同的数据构造函数,必须始终用于构造和模式匹配其类型的值。即使一开始您可能认为可以从上下文中猜测它,但它就是不这样做。
还有其他原因,更具技术性。首先,惰性评估的规则是根据数据构造函数定义的。简短的版本:惰性评估意味着如果Haskell被迫窥视
类型的值内部,也许是
,它将尝试执行所需的最低工作量,以确定它看起来像无
还是像Just x
-最好它不会在执行此操作时窥视x
内部。
第二:语言需要能够区分
也许是
、也许(也许是a)
和也许(也许(也许是a))
等类型。如果你考虑一下,如果我们有一个像你写的那样工作的类型定义:
data Maybe a = Nothing | a -- NOT VALID HASKELL
...我们想制作一个类型为
Maybe(Maybe a)
的值,你无法区分这两个值:
example5, example6 :: Maybe (Maybe a)
example5 = Nothing
example6 = Just Nothing
乍一看,这可能有点傻,但假设您有一个值为“null”的映射:
-- 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)
...想要查找一个条目:
Map.lookup :: Ord k => Map k v -> k -> Maybe v
所以如果我们在地图上查找
Mary
,我们有:
Map.lookup favoriteNumber mary :: Maybe (Maybe Int)
现在结果
无
意味着玛丽不在地图上,而只是无
意味着玛丽在地图上,但她没有最喜欢的号码。
Haskell的代数数据类型被标记为并集。按照设计,当您将两个不同的类型组合成另一个类型时,它们必须有构造函数来消除歧义。
数据类型与代数定义不匹配。
data Maybe a = Nothing | a
这里没有a
的“标签”。在你的情况下,我们如何区分正常的、未包装的a
?
可能
有一个构造函数,因为它必须设计一个构造函数。
其他语言确实有联合类型,可以像你想象的那样工作,但它们不适合Haskell。它们在实践中的表现不同,而且往往有点容易出错。
与普通的联合类型相比,更倾向于使用标记的联合有一些强大的设计原因。他们擅长类型推断。实际代码中的联合通常都有一个标签。而且,从优雅的角度来看,标记的并集自然适合这种语言,因为它们是产品(即元组和记录)的对偶。如果你好奇的话,我在一篇介绍和激励代数数据类型的博客文章中写到了这一点。
脚注
我在两个地方使用了联合类型:TypeScript和C。TypeScript编译为动态类型的JavaScript,这意味着它在运行时跟踪值的类型,基本上是一个标记。
C没有,但在实践中,大约90%的联合类型的使用要么有标记,要么有效地模拟结构子类型。我的一位教授实际上做了一项实证研究,研究了在真正的C代码中如何使用并集,但我不记得这是一篇什么论文。
问题内容: 我知道该包是由我们编写的每个Java程序自动导入的,因此包中的所有类对我们都是自动可用的。 我的问题是为什么也不要自动导入和其他软件包?那肯定会节省一些打字:) 因此,请解释为什么不这样做。 问题答案: 不自动导入过多的一个好理由是避免名称空间冲突。例如,如果其中的所有内容都是自动导入的,那么您想引用另一个名为“ Map”的类,则必须使用其完全限定的名称来引用它。 响应于该线程中的其他
本文向大家介绍为什么HTML5只需要写就可以?相关面试题,主要包含被问及为什么HTML5只需要写就可以?时的应答技巧和注意事项,需要的朋友参考一下 HTML5 之前基于 SGML, SGML 需要指定 DTD 解析文档, 可通过<!DOCTYPE>指定要使用的 DTD, 若不写则会进入怪异模式; HTML5 不基于 SGML, 不需要指明 DTD, 其<!DOCTYPE>只有一种: .
问题内容: 这是我的代码: 问题答案: 像所有的专用方法(用“魔法姓名”开始和结束),是 不是 意味着要直接调用(除非是非常特殊的情况下,诸如最多=调用父类):确切地说,这种方法被称为的一部分内置程序和运算符的操作。在的情况下,所讨论的运算符是“包含检查”运算符。 在展示您的班级时(除了修正您的错字,并使用!!代替),并将其作为实例打印-以及对其他任何包含检查的打印,因为总会返回(因为,非空值字符
在这里你可以下载一个acroform字段的pdf,他的大小正好是427KB 如果我删除这个唯一的字段,文件只有3KB,为什么会发生这种情况?我试着用PDF调试器分析,对我来说没有什么奇怪的。
问题内容: 如果一个元组是不可变的,那么为什么它可以包含可变项呢? 似乎矛盾的是,当可变项(例如列表)确实被修改时,它所属的元组保持不变。 问题答案: 这是一个很好的问题。 关键的见解是,元组无法知道其中的对象是否可变。使对象可变的唯一方法是拥有一种更改其数据的方法。通常,无法检测到此情况。 另一个见解是Python的容器实际上不包含任何东西。相反,它们保留对其他对象的引用。同样,Python的变
问题内容: 苹果的新框架似乎使用了一种 新型语法 ,可以有效地构建元组,但又具有另一种语法: 尝试解决这种语法的实际含义时 ,我发现这里使用的初始化程序将类型的闭包 作为第二个参数,其中的通用参数是通过闭包推断的。为了找出要推断的类型,我对代码进行了一些更改,并保持其功能: 以此,表明自己是类型,即类型。向上看,我发现它是一个源自自身的包装器类型,只能通过传递应该包装的元组来进行初始化。 题 现在