Haskell 理解折叠树函数';s型派生

Haskell 理解折叠树函数';s型派生,haskell,types,tree,type-inference,fold,Haskell,Types,Tree,Type Inference,Fold,查看数据树中出现的此定义: foldTree::(a->[b]->b)->树a->b foldTree f=去哪里 go(节点x ts)=fx(映射go ts) 我的具体问题是:由于go名称出现在(map go ts)中等式的右侧,函数的类型是什么 (a->[b]->b) 被推断 例如,拥有这行代码: foldTree(:)(节点1[节点2[])) 对定义进行实例化的示例: foldTree(:)=去哪里 go(节点1[节点2[])=(:)1(映射go[节点2[])) (:)1(map

查看
数据树中出现的此定义:

foldTree::(a->[b]->b)->树a->b
foldTree f=去哪里
go(节点x ts)=fx(映射go ts)
我的具体问题是:由于
go
名称出现在
(map go ts)
中等式的右侧,函数的类型是什么

(a->[b]->b)
被推断

例如,拥有这行代码:

foldTree(:)(节点1[节点2[]))
对定义进行实例化的示例:

foldTree(:)=去哪里
go(节点1[节点2[])=(:)1(映射go[节点2[]))
(:)1(map go[Node 2[])
没有完全计算,所以我只看到
(:)1
具有类型
Num a=>[a]->[a]
。但是,缺少一个缺口,为了填补这个缺口,应该完成递归。因此,在计算类型时似乎存在一些循环


任何见解都值得赞赏。

哈斯克尔的类型推断非常聪明!我不能告诉你这是怎么推断出来的,但让我们来看看它可能是怎样的。现实可能并不遥远。在这种情况下,实际上不需要类型签名

foldTree f = go where
    go (Node x ts) = f x (map go ts)
foldTree
被定义为获取参数,
go
被定义为获取参数,因此我们从一开始就知道这些是函数

foldTree::_a->\u b
foldTree f=去哪里
go::_c->\u d
go(节点x ts)=fx(映射go ts)
现在我们看到,
f
是用两个参数调用的,所以它实际上必须是(至少)两个参数的函数

foldTree::(\ux->\uy->\uz)->\ub
foldTree f=去哪里
go::_c->\u d
go(节点x ts)=fx(映射go ts)
由于
foldTree f=go
go::\u c->\u d
,因此结果类型
\u b
实际上必须是
\u c->\u d
*:

foldTree::(\ux->\uy->\uz)->\uc->\ud
foldTree f=去哪里
go::_c->\u d
go(节点x ts)=fx(映射go ts)
传递给
f
(类型为
\u y
)的第二个参数是
map go ts
。由于
go::\u c->\u d
\u y
必须是
[\u d]

foldTree::(\ux->[\ud]->\uz)->\uc->\ud
foldTree f=去哪里
go::_c->\u d
go(节点x ts)=fx(映射go ts)
go
将其参数与
节点x ts
匹配,并且
节点
树的数据构造函数,因此
go
的参数(
\u c
)必须是

foldTree::(\ux->[\ud]->\uz)->Tree\up->\ud
foldTree f=去哪里
go::Tree\u p->\u d
go(节点x ts)=fx(映射go ts)
节点
构造函数的第一个字段作为
f
的第一个参数传递,因此
\u x
\u p
必须相同:

foldTree::(\ux->[\ud]->\uz)->Tree\ux->\ud
foldTree f=去哪里
go::Tree x->\uD
go(节点x ts)=fx(映射go ts)
由于
go
被定义为
f
,它们必须具有相同类型的结果,因此
\u z
\u d

foldTree::(\ux->[\ud]->\ud)->Tree\ux->\ud
foldTree f=去哪里
go::Tree x->\uD
go(节点x ts)=fx(映射go ts)
呼。现在,编译器检查以确保这些类型有效(它们确实有效),并将它们从“元变量”(意味着推理机不知道它们代表什么类型的变量)“泛化”为量化类型变量(绝对多态的变量),然后

foldTree::for all a b。(a->[b]->b)->树a->b
foldTree f=去哪里
go::树a->b
go(节点x ts)=fx(映射go ts)
现实是有点复杂,但这应该给你的要点


[*]这一步有点作弊。我忽略了一个名为“let泛化”的功能,它在本文中不需要,实际上被GHC Haskell中的几个语言扩展所禁用。

Haskell的类型推断非常聪明!我不能告诉你这是怎么推断出来的,但让我们来看看它可能是怎样的。现实可能并不遥远。在这种情况下,实际上不需要类型签名

foldTree f = go where
    go (Node x ts) = f x (map go ts)
foldTree
被定义为获取参数,
go
被定义为获取参数,因此我们从一开始就知道这些是函数

foldTree::_a->\u b
foldTree f=去哪里
go::_c->\u d
go(节点x ts)=fx(映射go ts)
现在我们看到,
f
是用两个参数调用的,所以它实际上必须是(至少)两个参数的函数

foldTree::(\ux->\uy->\uz)->\ub
foldTree f=去哪里
go::_c->\u d
go(节点x ts)=fx(映射go ts)
由于
foldTree f=go
go::\u c->\u d
,因此结果类型
\u b
实际上必须是
\u c->\u d
*:

foldTree::(\ux->\uy->\uz)->\uc->\ud
foldTree f=去哪里
go::_c->\u d
go(节点x ts)=fx(映射go ts)
传递给
f
(类型为
\u y
)的第二个参数是
map go ts
。由于
go::\u c->\u d
\u y
必须是
[\u d]

foldTree::(\ux->[\ud]->\uz)->\uc->\ud
foldTree f=去哪里
go::_c->\u d
go(节点x ts)=fx(映射go ts)
go
将其参数与
节点x ts
匹配,并且
节点
树的数据构造函数,因此
go
的参数(
\u c
)必须是

foldTree::(\ux->[\ud]->\uz)->Tree\up->\ud
foldTree f=去哪里
go::Tree\u p->\u d
go(节点x ts)=fx(映射go ts)
节点
构造函数的第一个字段作为
f
的第一个参数传递,因此
\ux