解释高阶函数的类型签名?

时间:2015-10-29 01:32:10

标签: haskell type-signature

blue {
width: 100px;
height: 100px;
background: blue;
float: left;
position: relative;
} 

#red {
width: 100px;
height: 50px;
background: red;
float:right;
}

#green {
width: 100px;
height: 50px;
background: green;

float: right;
clear: both;
}
function :: (t2 -> t1) -> (t1 -> t) -> t2 -> t
function f1 f2 x = f2 (f1 x )

function1 :: (t -> t) -> t -> t
function1 f1 x = f1 (f1 x)

任何人都可以解释前两个的类型签名以及为什么最后3个的类型签名是相同的吗?我正在阅读有关高阶函数的内容以及它们没有进入我的脑海。

2 个答案:

答案 0 :(得分:4)

  

有人可以解释前两个的类型签名吗?

让我们为类型变量提供一些人类可读的名称。

function :: (in -> tmp) -> (tmp -> out) -> in -> out
function f1 f2 x = f2 (f1 x )

这种类型说:如果你给我一个功能,将输入输入到临时值,以及一个将临时值输入输出的功能,我会给你一个功能,将输入输入到输出中。您可以想象绘制一个"数据处理管道",其中我们有一堆各种类型的对象,以及将一种类型的值转换为另一种类型的值的操作。例如,在这里我可以直观地绘制您的function

in    ----- f1 ----->    tmp    ----- f2 ----->    out
  \                                               ^
   \                                             /
    `------------- function f1 f2 --------------'

这里的预期解释是,您可以想象在此管道中采用两条路线 - 您首先使用操作f1,然后使用操作f2;或者你可以使用function f1 f2操作一次完成整个事情。

function1 :: (t -> t) -> t -> t
function1 f1 x = f1 (f1 x)

此处,function1采用函数将t s转换为其他t s,并返回一个(可能不同的)函数来转换t s(再次,进入其他t s。再次,为此绘制数据处理管道,您可能会得到这样的结果:

t    ----- f1 ----->    t    ----- f1 ----->    t
 \                                             ^
  \                                           /
   `------------- function1 f1 --------------'
  

为什么最后3个的类型签名相同?

让我们快速了解最后三个函数的定义:

function2 f1 f2 x = f1 (f1 x)
function3 f1 f2 x = f1 (f1 (f1 x))
function4 f1 f2 x = f1 (f1 (f1 x))

好的,关于function2function3function4的第一个有趣的事情是它们有三个参数,f1f2和{ {1}},但是每个人都完全忽略了x - 它们在f2的右侧从未被提及过。其次,=function3以完全相同的方式定义,因此它们可以被赋予相同的类型并不奇怪。

考虑到这两个方面,我们可以稍微缩小一下这个问题,以便我们问为什么这两个函数都有类型签名:

function4

具体来说,他们的共享类型签名是:

function2' f x = f (f x)
function3' f x = f (f (f x))

正如我们上面针对(t -> t) -> (t -> t) 所讨论的那样,这意味着它们会在function1上进行转换,并且它们也会在t上产生转换。再次考虑数据处理流水线,现在应该很清楚为什么它们具有相同的类型:我们是否在管道中应用我们的函数t两次或三次并不重要;这种类型只是改变:

f

假设 .------------------------ function3' f ------------------------. / \ / v t ----- f -----> t ----- f -----> t ----- f -----> t \ ^ \ / `------------ function2' f -------------' 是一个从标有f的节点开始并在标有t的节点处结束的函数,那么无论我们在停止之前应用t多少次,我们总是回到一个函数,该函数从标有f的节点开始,到标有t的节点结束。

答案 1 :(得分:2)

当一个函数将其他函数作为输入参数或将其他函数作为值返回时,它被称为高阶函数。

在第一种情况下,名为function的函数有两个函数 类型(t2 -> t1)(t1 -> t)以及类型t2的值。您可以 与ghci一起玩,以更好地了解它的作用:

λ> :t id
id :: a -> a
λ> id 3
3
λ> function id id 3
3
λ> function id (\x -> x + 3) 3
6
λ> function (\x -> x + 3) (\x -> x + 3) 3
9

function1中,您将单个函数和t类型的值视为。{ 输入

  

为什么最后3个的类型签名是相同的?

因为他们采取相同的输入。编译器非常智能,可以推断出您传递的第二个参数是类型t -> t1的函数,即使您没有给出任何括号。我建议你给括号,因为它提高了可读性。

Daniel Wagner指出,你在前三个定义中没有使用f2输入。所以,你甚至可以传递一个底值,它不需要是一个函数:

λ> function4 id undefined "hello"
"hello"