So as it turns out, you tried to implement U combinator, which is not a fixed-point combinator.
Whereas the fixed-point Y combinator _Y g = g (_Y g)
enables us to write
_Y (\r x -> if x==0 then 1 else x * r (x-1)) 5 -- _Y g => r = _Y g
-- 120
with _U g = g (g)
we'd have to write
_U (\f x -> if x==0 then 1 else x * f f (x-1)) 5
-- _U g => f = g, f f == _U g
As you've discovered, this _U
has no type in Haskell. On the one hand g
is a function, g :: a -> b
; on the other it receives itself as its first argument, so it demands a ~ a -> b
for any types a
and b
.
Substituting a -> b
for a
in a -> b
right away leads to infinite recursion (cf. "occurs check"), because (a -> b) -> b
still has that a
which needs to be replaced with a -> b
; ad infinitum.
A solution could be to introduce a recursive type, turning a ~ a -> b
into R = R -> b
i.e.
newtype U b = U {app :: U b -> b} -- app :: U b -> U b -> b
so we can define
_U :: (U b -> b) -> b
_U g = g (U g) -- g :: U b -> b
-- = app (U g) (U g)
-- = join app (U g)
-- = (join app . U) g -- (**)
which now has a type; and use it as
_U (\f x -> if x==0 then 1 else x * app f f (x-1)) 5
-- _U g => f = U g,
-- 120 -- app f f = g (U g) == _U g
edit: And if you want to implement the Y combinator as a non-recursive definition, then
_U (\f x -> if x==0 then 1 else x * (app f f) (x-1))
= -------.- -- abstraction
_U (\f -> (\r x -> if x==0 then 1 else x * r (x-1)) (app f f))
= -------.--------------------------------- -- abstraction
(\g -> _U (\f -> g (app f f))) (\r x -> if x==0 then 1 else x * r (x-1))
= -- r = app f f
_Yn (\r x -> if x==0 then 1 else x * r (x-1))
so
_Yn :: (b -> b) -> b
_Yn g = _U (\f -> g (app f f)) -- Y, non-recursively
does the job:
_Yn (\r x -> if x==0 then 1 else x * r (x-1)) 5
-- 120
(later update:) Or, point-freer,
_Yn g = _U (\f -> g (app f f))
= _U (\f -> g (join app f))
= _U (\f -> (g . join app) f)
= _U (g . join app)
= _U ( (. join app) g )
Thus
_Yn :: (b -> b) -> b
_Yn = _U . (. join app) -- and, using (**)
= join app . U . (. join app)