我是一名C#开发人员,正在阅读"Real World Haskell"这本书,以便真正理解函数式编程。这样当我学习F#时,我就可以真正领会它,而不仅仅是“用C#代码写F#代码”。
今天,我遇到了一个例子,我认为我已经理解了3次,然后才发现漏掉了某些东西,更新了我的解释,并递归(也诅咒过,相信我)。
现在我相信我确实理解了它,并在下面写了一个详细的“英文解释”。Haskell专家们可以确认我的理解,或指出我所错过的内容吗?
注意:Haskell代码片段(直接引用自书中)定义了一个自定义类型,该类型旨在与内置的Haskell列表类型同构。
Haskell代码片段
data List a = Cons a (List a)
| Nil
defining Show
编辑:经过一些回应,我发现了一个误解,但是我不太清楚Haskell的“解析”规则会纠正这个错误。所以我包括了我的原始(不正确的)解释,然后是一个更正,最后是仍然不清楚的问题。
编辑:这是我原来(不正确的)的代码片段“英语解释”
- 我正在定义一个类型称为“List”。
- List类型是参数化的。它有一个类型参数。
- 有两个值构造函数可以用于创建List实例。其中一个值构造函数称为“Nil”,另一个值构造函数称为“Cons”。
- 如果使用“Nil”值构造函数,则没有字段。
- “Cons”值构造函数具有单个类型参数。
- 如果使用“Cons”值构造函数,则必须提供2个字段。第一个必需字段是List的实例。第二个必需字段是a的实例。
- (我有意省略了有关“定义Show”的任何内容,因为它不是我现在想重点关注的部分)。
更正的解释如下(改动用粗体标出)
- 我正在定义一个类型称为“List”。
- List类型是参数化的。它有一个类型参数。
- 有两个值构造函数可以用于创建List实例。其中一个值构造函数称为“Nil”,另一个值构造函数称为“Cons”。
如果使用“Nil”值构造函数,则没有字段。
5.(此行已被删除…不准确)“Cons”值构造函数具有单个类型参数。
如果使用“Cons”值构造函数,则必须提供2个字段。第一个必需字段是a的实例。第二个必需字段是“list-of-a”的实例。
- (我有意省略了有关“定义Show”的任何内容,因为它不是我现在想重点关注的部分)。
仍然不清楚的问题
最初的困惑是关于代码片段中读取“Cons a(List a)”部分的内容。事实上,这是仍然不清楚的部分。
人们指出,“Cons”标记后每个项目都是一个类型,而不是值。因此,这一行表示“Cons值构造函数有2个字段:类型为'a'和类型为'list-of-a'的另一个字段。”
那非常有帮助。但是仍然有些不清楚。当我使用Cons值构造函数创建实例时,这些实例将第一个'a'“解释”为意思是“在此处放置传递的值”。但是他们不会以相同的方式解释第二个'a'。
例如,请考虑此GHCI会话:
*Main> Cons 0 Nil
Cons 0 Nil
*Main> Cons 1 it
Cons 1 (Cons 0 Nil)
*Main>
当我键入“Cons 0 Nil”时,它使用“Cons”值构造函数创建List的一个实例。从0中,它了解到类型参数是“Integer”。到目前为止,没有混淆。然而,它还确定第一个字段的值为0。但它对第二个字段的值什么也不确定...它只确定第二个字段的类型为“List Integer”。
因此,我的问题是,为什么第一个字段中的“a”意味着“这个字段的类型是'a',值是'a'”,而第二个字段中的“a”仅意味着“这个字段的类型是 'List of a'”?
编辑:我相信现在我已经看到了光明,多亏了几个回复。让我在这里表述一下。(如果某种方式仍然有错误,请告诉我!)
在片段“Cons a(List a)”中,我们正在说“Cons”值构造函数具有两个字段,第一个字段的类型为'a',第二个字段的类型为“List of a”。
这就是我们说的全部内容!特别是,我们什么也没说关于值!这是我缺少的一个关键点。
后来,我们想要创建一个实例,使用“Cons”值构造函数。我们将其键入解释器:“Cons 0 Nil”。这明确地告诉Cons值构造函数使用0作为第一个字段的值,并使用Nil作为第二个字段的值。
就是这样。一旦你知道值构造函数定义仅指定类型,一切就会变得清楚。
感谢大家的帮助回复。正如我所说,如果还有问题,请告诉我。谢谢。