Well, y
has to be of type (a -> b) -> c
, for some a
, b
and c
we don't know yet; after all, it takes a function, f
, and applies it to an argument, so it must be a function taking a function.
Since y f = f x
(again, for some x
), we know that the return type of y
must be the return type of f
itself. So, we can refine the type of y
a bit: it must be (a -> b) -> b
for some a
and b
we don't know yet.
To figure out what a
is, we just have to look at the type of the value passed to f
. It's y f
, which is the expression we're trying to figure out the type of right now. We're saying that the type of y
is (a -> b) -> b
(for some a
, b
, etc.), so we can say that this application of y f
must be of type b
itself.
So, the type of the argument to f
is b
. Put it all back together, and we get (b -> b) -> b
— which is, of course, the same thing as (a -> a) -> a
.
Here's a more intuitive, but less precise view of things: we're saying that y f = f (y f)
, which we can expand to the equivalent y f = f (f (y f))
, y f = f (f (f (y f)))
, and so on. So, we know that we can always apply another f
around the whole thing, and since the "whole thing" in question is the result of applying f
to an argument, f
has to have the type a -> a
; and since we just concluded that the whole thing is the result of applying f
to an argument, the return type of y
must be that of f
itself — coming together, again, as (a -> a) -> a
.
?banner=none
to Wikipedia URLs :) – Cuckooλf.(λx.f (x x)) (λx.f (x x))
. However, this cannot be simply typed in Haskell (and is impossible to type in simply-typed lambda calculus; pun intended). The need for self application(x x)
requires recursion at type level. In Haskell, you can avoid type recursiona = a -> a
by wrapping it intonewtype Rec a = In { out :: Rec a -> a }
. – Seclusive