如何解释Haskell中的函数类型?

时间:2011-10-02 22:22:03

标签: function haskell

我的代码如下

data Tree a = Leaf a | Node (Tree a) (Tree a) 
    deriving (Show)
f x = x + 1
myTree = Node (Node (Leaf 2) (Leaf 3)) (Leaf 4)
maptree f (Leaf a)= Leaf (f a)
maptree f (Node xl xr ) = Node (maptree f xl) (maptree f xr)

可以在树中的Leaf元素中添加一个。

我进一步查看

的函数类型
*Main> let maptree f (Leaf a)= Leaf (f a) 
maptree :: (t -> a) -> Tree t -> Tree a 

*Main> let maptree f (Node  xl xr ) = Node (maptree f xl) (maptree f xr) 
maptree :: t -> Tree t1 -> Tree a

t,a和t1在这里意味着什么?是否有任何我可以阅读的参考资料?

感谢。

2 个答案:

答案 0 :(得分:3)

在类型上下文中使用时,以小写字母开头的标识符(如tat1)是类型变量。它们是占位符,可以专门用于任何类型。有关如何阅读类型签名的详细信息,请参阅this answer

另请注意,在GHCi中,您的示例将首先定义一个仅处理Leaf情况的函数,然后将其替换为仅处理Node情况的另一个函数,这与Haskell源文件不同他们将是两个相同功能的案例。要为GHCi中的函数指定多个方程式,请用分号分隔它们:

*Main> let maptree f (Leaf a) = Leaf (f a); maptree f (Node xl xr) = Node (maptree f xl) (maptree f xr)

或使用多线模式:

*Main> :{
*Main| let maptree f (Leaf a) = Leaf (f a)
*Main|     maptree f (Node xl xr) = Node (maptree f xl) (maptree f xr)
*Main| :}

答案 1 :(得分:2)

我们将从第一个签名开始:

(t -> a) -> Tree t -> Tree a

这基本上说,“如果某个函数采用t类型并生成a以及Tree包含t类型的项,则生成{ {1}}包含Tree类型的元素。

at完全是GHCi生成的任意名称;我们可以很容易地说:

a

虽然大多数程序员会写:

(originalType -> newType) -> Tree originalType -> Tree newType

现在,第二个签名:

(a -> b) -> Tree a -> Tree b

这个签名很奇怪,因为像Hammar所指出的那样,你在GHCi中写的两个t -> Tree t1 -> Tree a 定义是完全独立的。所以让我们看看第二个定义:

maptree

这里,maptree f (Node xl xr) = Node (maptree f xl) (maptree f xr) 的类型并不重要,因为你没有将它应用于任何参数,你只将它传递给f,递归不关心什么类型maptree是的。所以,让我们给f类型f,因为它没关系。

现在,类似地,txl没有约束,因为它们只传递给xr,它不知道它们的类型,除了它应该是{{1} }}。因此,我们不妨将其类型称为maptree

我们知道这个函数会因Tree构造函数而返回Tree t1,但我们看到的前两种类型与树中元素的类型无关,所以我们也可以称之为Tree

举个例子,让我们看看当我们扩展以下内容时会发生什么:

Node

然后失败,因为在这种情况下Tree a无法扩展maptree True (Node (Leaf 0) (Leaf 1)) = Node (maptree True (Leaf 0)) (maptree True (Leaf 1))

然而,类型可以解决:

maptree

所以签名很奇怪,但确实有意义。我想,请记住不要覆盖定义。