为什么这个函数的类型(a - > a) - >一个?
Prelude> let y f = f (y f)
Prelude> :t y
y :: (t -> t) -> t
它不应该是无限/递归类型吗? 我打算尝试说出我认为类型应该是什么,但我出于某种原因无法做到。
y :: (t -> t) -> ?WTFIsGoingOnOnTheRHS?
我不知道f(y f)如何解析为某个值。以下对我来说更有意义:
Prelude> let y f x = f (y f) x
Prelude> :t y
y :: ((a -> b) -> a -> b) -> a -> b
但它仍然令人费解。发生了什么事?
答案 0 :(得分:29)
嗯,y
必须属于(a -> b) -> c
类型,对于某些a
,b
和c
我们还不知道;毕竟,它需要一个函数f
,并将其应用于一个参数,因此它必须是一个函数函数。
从y f = f x
开始(同样,对于某些x
),我们知道y
的返回类型必须是f
本身的返回类型。因此,我们可以稍微改进y
的类型:对于某些我们尚不知道的(a -> b) -> b
和a
,它必须是b
。
要弄明白a
是什么,我们只需要查看传递给f
的值的类型。它是y f
,这是我们现在试图找出类型的表达式。我们说y
的类型是(a -> b) -> b
(对于某些a
,b
等等,所以我们可以说{{1}的这个应用}必须是y f
类型本身。
因此,b
的参数类型为f
。把它们全部重新组合在一起,我们得到b
- 这当然与(b -> b) -> b
相同。
这是一个更直观但不太精确的事物视图:我们说的是(a -> a) -> a
,我们可以将其扩展为等效的y f = f (y f)
,y f = f (f (y f))
,等等。所以,我们知道我们总是可以在整个事情中应用另一个y f = f (f (f (y f)))
,因为所讨论的“整件事”是将f
应用于参数的结果,f
必须有类型f
;因为我们刚刚得出结论,整个事情是将a -> a
应用于参数的结果,f
的返回类型必须是y
本身的返回类型 - 再次聚集在一起{ {1}}。
答案 1 :(得分:9)
@ 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
您可以按照您喜欢的任何功能执行相同的步骤,看看会发生什么。这两个例子都汇集到了价值观,但并不总是这样。
答案 2 :(得分:9)
只需两点即可添加到其他人的答案中。
您定义的函数通常称为fix
,它是fixed-point combinator:计算另一个函数的fixed point的函数。在数学中,函数f
的固定点是参数x
,使得f x = x
。这已经允许您推断fix
的类型必须是(a -> a) -> a
; “从a
到a
执行函数的函数,并返回a
。”
您已调用函数y
,它似乎位于Y combinator之后,但这是一个不准确的名称:Y组合子是一个特定的固定点组合子,但与你在这里定义的那个不一样。
我不知道f(y f)如何解析为一个值。
嗯,诀窍是Haskell是一种非严格的(a.k.a。“懒惰”)语言。如果f (y f)
在所有情况下都不需要评估其f
参数,则y f
的计算可以终止。因此,如果您正在定义阶乘(如John L所示),fac (y fac) 1
在不评估y fac
的情况下评估为1。
严格的语言无法做到这一点,因此在这些语言中,您无法以这种方式定义定点组合子。在这些语言中,教科书定点组合器是Y组合器本身。
答案 3 :(得分:6)
让我讲一个组合子。它被称为“fixpoint combinator”,它具有以下属性:
属性:“fixpoint combinator”采用函数f :: (a -> a)
,发现该函数的“固定点”x :: a
,以便f x == x
。固定点组合器的某些实现在“发现”时可能更好或更差,但假设它终止,它将产生输入函数的固定点。任何满足The Property的函数都可以称为“fixpoint combinator”。
称此为“fixpoint combinator”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
:
y f == f (y f)
。您可以假设y f
表示不同的计算,而不是假设x
发现x
,并且仍然得出相同的结论(iinm)。
由于您的函数满足The Property,我们可以得出结论它是一个fixpoint combinator,并且我们声明的其他属性(包括类型)适用于您的函数。
这不是一个可靠的证明,但我希望它能提供额外的见解。