如何获取给定多态函数和表达式的类型?

时间:2016-05-21 14:54:15

标签: haskell typing

这是提示这篇文章的考试问题:

考试问题:

考虑Haskell中二进制树的以下定义。

data Tree a = Empty | Leaf a | Node (Tree a) a (Tree a)

根据此声明,树可以是:EmptyLeaf包含类型为a的元素,或Node包含类型为{{1}的元素}和两个a s。

例如,以下表达式的类型为Tree

Tree Int

以下函数将Leaf 0 Node (Leaf 1) 2 (Leaf 3) 展平为元素列表(回想一下Tree是Haskell中列表的串联运算符)。

++

我。为函数flatten Empty = [ ] flatten (Leaf l) = [l] flatten (Node t1 r t2) = (flatten t1) ++ (r : (flatten t2)) 提供多态类型。证明你的答案。

II。为表达式提供一个类型:

flatten

证明你的答案。

我的问题

我认为我理解这里的理论,很明显flatten (Node (Leaf 1) 2 (Leaf 3)) 为了完成工作,接受多种类型(flatten[ ]和{{1}但是我该怎么解释它的类型呢?除非我假设Leaf与它接受的类型相同,否则我不确定从何处开始。

关于问题的第二部分,Node是否有不同的类型,具体取决于它是否以表达式的形式出现?如果是这样的话,我觉得我在最后一段中的假设是正确的,但我希望能够在我身边获得实际逻辑的安慰,而不仅仅是猜测。

提前致谢。

2 个答案:

答案 0 :(得分:2)

要正确理解这个问题,您应该了解type inferencedifference between type and data constructor。 Haskell使用类型推断算法,但是考试问题的措辞是因为它的知识不是必需的,直觉就足够了。知道如果在GHCI中键入:t <expression>,它会为您提供类型,这也很有用。

在此特定示例中,您的定义创建了四个构造函数 - 一元类型构造函数Tree,nullary数据Empty,一元数据Leaf和三元数据Node

现在,我们来看看这个功能。函数有三个基于模式匹配的定义,但是我们知道每个定义必须具有相同的类型(或者与其他类型一致)。从第一行开始

flatten Empty = [ ]

我们可以看到flatten需要一些Empty(我们知道的是Tree的数据构造函数),并返回一个[],什么是列表。但是,Tree是参数化类型。 Empty然而不使用参数化,因此它可以属于任何Tree - 多态类型Tree a[]是一个列表。列表通常是[a]类型,a是列表成员的类型。空列表中没有成员,因此列表的类型是多态的 - 保持[a]。然而,我们说a来自Tree参数,并且这行定义中没有任何内容表明列表成员来自树,所以我们可以给它另一个名字,让我们一起去b。从第一行开始,我们推断出类型为Tree a -> [b],或者可以统一的东西。

从第二行开始

flatten (Leaf l) = [l]

我们看到flatten需要一些Leaf,参数化为l。我们知道Leaf lTree a的数据构造函数,我们知道la的类型。所以该函数再次需要一些Tree a。它返回单个元素的列表l。我们知道la的类型,因此列表必须是[a]类型。整个类型是Tree a -> [a]

我们可以采用这种类型并将其与第一行的结果统一起来。如果Tree a -> [b]应与Tree a -> [a]相同,我们会发现a必须与b相同。所以我们统一起来。我们应该看第三行,但在这种情况下,类型不会进一步统一。

还有一件事。如果我们有这样的定义:

flatten Empty = ['e']
flatten (Leaf l) = [l]
flatten (Node t1 r t2) = (flatten t1) ++ (r : (flatten t2))

我们会从第一行看到,结果类型是字符数组[Char](或String,它是别名)。函数类型为Tree a -> [Char]。在考虑第二行并且看到数组包含叶子包含的内容之后,在统一之后我们必须假设树只包含字符,并且函数的类型将是Tree Char -> [Char]

答案 1 :(得分:1)

  很明显flatten为了完成自己的工作,接受多种类型([ ]LeafNode

我不明白为什么你认为flatten接受[ ]。我甚至不确定你的意思; [ ]不是类型,它是一种类型构造函数,它将类型a作为参数并返回类型为{{[a]类型的列表1}}”。

定义a的三个子句都使它成为一个带有一个参数的函数,因此它有一个flatten形式的类型,其中x -> y是参数的类型,{ {1}}是返回类型。在每种情况下,参数都是通过应用x类型构造函数的构造函数形成的,因此参数的类型为y。第一个子句中的返回值为Tree,因此其类型的格式为Tree w。到目前为止,我们知道最常见的[]类型的格式为[z]。第二个子句flatten提供了一个额外的约束Tree w -> [z],因为flatten (Leaf l) = [l]的类型出现在双方,所以最常见的类型是w = z形式。

这实际上是l的有效类型,因此Tree z -> [z]是最常见的flatten类型。要查看此内容,您需要完成整个定义并检查它是否对Tree z -> [z]施加了任何其他约束。

这是一种多态类型:它包含类型变量flatten。此变量可以通过任何类型实例化。例如,z使用z类型为flatten (Leaf (1 :: Int)),而flatten使用类型为Tree Int -> [Int]的{​​{1}}。

flatten (Leaf True)是多态的这一事实并不是因为它是一个函数。将它应用于参数并不会改变flatten的类型,但它可能不会完全使用它。结果表达式的类型仍然可以是多态的。例如,所有Tree Bool -> [Bool]的{​​{1}}类型为flatten,与flatten相同,因为\x -> flatten x等同于Tree a -> [a],所以不出所料。< / p>

a的类型来自三个约束:flatten,其参数类型为\x -> flatten x,其返回类型为flattenflatten (Leaf 3)接受任何约束键入flatten并返回Tree a[a]的类型为Leaf,即a的类型为Tree a,前提是此类型为3类的实例。因此Num a => a类型3的约束条件aNum的实例,即flatten (Leaf 3)具有最常规类型[a]