我的代码如下
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在这里意味着什么?是否有任何我可以阅读的参考资料?
感谢。
答案 0 :(得分:3)
在类型上下文中使用时,以小写字母开头的标识符(如t
,a
和t1
)是类型变量。它们是占位符,可以专门用于任何类型。有关如何阅读类型签名的详细信息,请参阅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
类型的元素。
a
和t
完全是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
,因为它没关系。
现在,类似地,t
和xl
没有约束,因为它们只传递给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
所以签名很奇怪,但确实有意义。我想,请记住不要覆盖定义。