为什么这个函数的类型(a - > a) - >一个?

时间:2012-01-18 23:20:33

标签: haskell types recursion anonymous y-combinator

为什么这个函数的类型(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

但它仍然令人费解。发生了什么事?

4 个答案:

答案 0 :(得分:29)

嗯,y必须属于(a -> b) -> c类型,对于某些abc我们还不知道;毕竟,它需要一个函数f,并将其应用于一个参数,因此它必须是一个函数函数。

y f = f x开始(同样,对于某些x),我们知道y的返回类型必须是f本身的返回类型。因此,我们可以稍微改进y的类型:对于某些我们尚不知道的(a -> b) -> ba,它必须是b

要弄明白a是什么,我们只需要查看传递给f的值的类型。它是y f,这是我们现在试图找出类型的表达式。我们说y的类型是(a -> b) -> b(对于某些ab等等,所以我们可以说{{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; “从aa执行函数的函数,并返回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,并且我们声明的其他属性(包括类型)适用于您的函数。

这不是一个可靠的证明,但我希望它能提供额外的见解。