Start with what you know, then figure out a bit here and a bit there until there are no unknowns.
Here is one possibility:
Call the unknown types FOO
, F
, G
, X
, and Y
, respectively.
Then look for something small and easy and start assigning types.
(g x)
is clearly an application of a function to one argument.
Set X
= a
and G
= a -> b
.
Then look at the enclosing expression:
(f x (g x) y)
| |
v v
a b
So far, we know that F
= a -> b -> Y -> C
, for some C
.
Go outwards again:
f (f x (g x) y) y
Since both x
and (f x (g x) y)
are first arguments to f
, they must be the same type a
, and the same idea applies to y
and (g x)
, giving them the type b
.
So, F
= a -> b -> b -> a
and, since the outer f
is only given two arguments, the type of the right-hand side must be b -> a
.
Thus
X = a
Y = b
G = a -> b
F = a -> b -> b -> a
FOO = (a -> b -> b -> a) -> (a -> b) -> a -> b -> (b -> a)
And, since arrows associate to the right, FOO
is equivalent to
(a -> b -> b -> a) -> (a -> b) -> a -> b -> b -> a