Haskell 为什么此函数的类型为(a->;a)->;A.
为什么这个函数的类型是(a->a)->a 它不应该是无限/递归类型吗? 我本想尝试用文字表达我认为应该是什么类型的,但由于某些原因,我就是做不到Haskell 为什么此函数的类型为(a->;a)->;A.,haskell,types,recursion,anonymous,y-combinator,Haskell,Types,Recursion,Anonymous,Y Combinator,为什么这个函数的类型是(a->a)->a 它不应该是无限/递归类型吗? 我本想尝试用文字表达我认为应该是什么类型的,但由于某些原因,我就是做不到 y :: (t -> t) -> ?WTFIsGoingOnOnTheRHS? 我不知道f(yf)是如何分解成一个值的。以下内容对我来说更有意义: Prelude> let y f x = f (y f) x Prelude> :t y y :: ((a -> b) -> a -> b) -> a -&
y :: (t -> t) -> ?WTFIsGoingOnOnTheRHS?
我不知道f(yf)是如何分解成一个值的。以下内容对我来说更有意义:
Prelude> let y f x = f (y f) x
Prelude> :t y
y :: ((a -> b) -> a -> b) -> a -> b
但它仍然令人费解。发生了什么事?嗯,
y
必须是(a->b)->c
,对于一些a
,b
和c
我们还不知道;毕竟,它接受一个函数,f
,并将其应用于一个参数,因此它必须是一个接受函数的函数
由于yf=fx
(同样,对于一些x
),我们知道y
的返回类型必须是f
本身的返回类型。因此,我们可以稍微细化一下y
的类型:对于一些a
和b
我们还不知道,它必须是(a->b)->b
为了弄清楚a
是什么,我们只需查看传递给f
的值的类型。它是yf
,这是我们现在试图找出的表达式类型。我们说的是y
的类型是(a->b)->b
(对于一些a
,b
,等等),所以我们可以说yf
的这个应用程序本身必须是b
类型
因此,f
的参数类型是b
。把它们放在一起,我们得到了(b->b)->b
,当然,这与(a->a)->a
是一样的
这里有一个更直观但不太精确的观点:我们说的是
yf=f(yf)
,我们可以将其扩展为等价的yf=f(f(yf))
,yf=f(f(yf))
,等等。因此,我们知道我们总是可以在整个事情周围应用另一个f
,并且由于所讨论的“整个事情”是将f
应用于参数的结果,f
必须具有a->a
类型;由于我们刚刚得出结论,整个过程是将f
应用于参数的结果,y
的返回类型必须是f
本身的返回类型-再次组合在一起,因为(a->a)->a
@ehird在解释类型方面做得很好,我想通过一些例子来说明它是如何解析为一个值的
f1 :: Int -> Int
f1 _ = 5
-- expansion of y applied to f1
y f1
f1 (y f1) -- definition of y
5 -- definition of f1 (the argument is ignored)
-- here's an example that uses the argument, a factorial function
fac :: (Int -> Int) -> (Int -> Int)
fac next 1 = 1
fac next n = n * next (n-1)
y fac :: Int -> Int
fac (y fac) -- def. of y
-- at this point, further evaluation requires the next argument
-- so let's try 3
fac (y fac) 3 :: Int
3 * (y fac) 2 -- def. of fac
3 * (fac (y fac) 2) -- def. of y
3 * (2 * (y fac) 1) -- def. of fac
3 * (2 * (fac (y fac) 1) -- def. of y
3 * (2 * 1) -- def. of fac
您可以对任何函数执行相同的步骤,看看会发生什么。这两个例子都收敛到值,但这并不总是发生。让我来介绍一个组合器。它被称为“不动点组合器”,具有以下特性: 属性:“不动点组合器”获取函数
f:(a->a)
,并发现该函数的“不动点”x::a
,从而fx==x
。不动点组合器的某些实现在“发现”方面可能更好或更差,但假设它终止,它将生成输入函数的固定点。任何满足该属性的函数都可以称为“不动点组合子”
称之为“定点组合器”y
。根据我们刚才所说的,以下是事实:
-- as we said, y's input is f :: a -> a, and its output is x :: a, therefore
y :: (a -> a) -> a
-- let x be the fixed point discovered by applying f to y
y f == x -- because y discovers x, a fixed point of f, per The Property
f x == x -- the behavior of a fixed point, per The Property
-- now, per substitution of "x" with "f x" in "y f == x"
y f == f x
-- again, per substitution of "x" with "y f" in the previous line
y f == f (y f)
好了。您已经根据不动点组合符的基本属性定义了y
:yf==f(yf)
。与假设yf
发现x
不同,您可以假设x
表示一个发散的计算,并且仍然得出相同的结论(iinm)
由于您的函数满足该属性,我们可以得出结论,它是一个不动点组合子,并且我们所述的其他属性(包括类型)适用于您的函数
这并不是一个确切的证据,但我希望它能提供更多的见解。只需对其他人的答案补充两点 您定义的函数通常称为
fix
,它是一个:计算另一个函数的。在数学中,函数f
的不动点是一个参数x
,使得fx=x
。这已经允许您推断fix
的类型必须是(a->a)->a
;函数,它将函数从a
转换为a
,并返回a
您调用了函数y
,它似乎在后面,但这是一个不准确的名称:y组合子是一个特定的固定点组合子,但与您在此处定义的不同
我不知道f(yf)是如何分解成一个值的
好吧,诀窍在于Haskell是一种非严格(又称“懒惰”)语言。如果在所有情况下f
不需要计算其yf
参数,则f(yf)
的计算可以终止。因此,如果定义阶乘(如John L所示),fac(y fac)1
计算为1,而不计算y fac
严格的语言无法做到这一点,因此在这些语言中,您不能用这种方式定义定点组合符。在这些语言中,教科书中的定点组合符就是Y组合符。假设这是真实的代码,那么不管是谁提出的,就解雇他。@MartinJames:嗯?你认为代码有什么问题?“这不是定义函数的最佳方法,但它是最简单的。”MartinJames,该函数是一个经过充分研究的函数,称为。(我想这是对的-我现在不能再检查维基百科!)不管怎样,也许你会因为如此庸俗而被解雇:-@AaronMcDaid:它实际上不是Y组合符,它只是等价于它;这是一个具有显式命名递归的定点函数,而Y combinator的创新之处是在没有任何语言支持的情况下实现递归。请注意,尝试添加
?banner=none-- as we said, y's input is f :: a -> a, and its output is x :: a, therefore
y :: (a -> a) -> a
-- let x be the fixed point discovered by applying f to y
y f == x -- because y discovers x, a fixed point of f, per The Property
f x == x -- the behavior of a fixed point, per The Property
-- now, per substitution of "x" with "f x" in "y f == x"
y f == f x
-- again, per substitution of "x" with "y f" in the previous line
y f == f (y f)