h1 f g x y = f (g x y) x
So, taking the argument list from left-to-right:
f is a function applied to two arguments: the first is the result of (g x y) and the second is x - we don't know what type the first argument is yet, so let's call it
a - we don't know the second type either, so let's call that
b - we also don't know the result type (so we'll call that
c), but we do know this must be the type returned by h1 - now
f is a function mapping a -> b -> c
g is a function applied to two arguments: the first is x and the second y - we know the first argument to
g is the same as the second argument to f, so it must be the same type: we already labelled that b - the second argument to
g is y, which we haven't assigned a placeholder type yet, so that gets the next in sequence, d g's result is the first argument to f, and we already labelled that a - so now
g is a function mapping b -> d -> a
- third argument is
x, and as that's the second argument to f, we've already labelled its type b - fourth argument is
y, which is the second argument to g, so we've already labelled its type d - the result of
h1 is the result of applying f to (g x y) x, as we said before, so it has the same type, already labelled c
Although we worked through the argument list in order, the actual process of labeling, inferring and unifying types for each of those arguments was done by looking at the body of h1.
So, my first bullet could be elaborated as:
f is the first argument to consider, so let's look at the body of h1 (everything after the =) to see how it's used f (g x y) x means that f is applied to (g x y) x, so f must be a function (g x y) is in parenthesis, which means whatever is inside those parentheses is being evaluated, and the result of that evaluation is an argument to f x is just a simple argument to f, passed straight from h1's own argument list - so,
f is a function taking two arguments
If it helps read f (g x y) x, you can consider the equivalent expression in C-like notation would be f(g(x,y), x). Here, you can see right away that f and g are functions taking two arguments, that f's first argument is whatever g returns, etc.
Note that the left-hand side of the expression, h1 f g x y, only gives one piece of type information by itself: h1 is a function on four arguments. The argument names themselves are just placeholders used in the right-hand side of the expression (the body of h1). The relative ordering of the arguments here just tells us how to call h1, but nothing about how h1 uses the arguments internally.
Again, here's a procedural-style equivalent (I'll use Python so I don't have to fill in any types):
def h1(f, g, x, y): return f(g(x,y),x)
this means exactly the same as
h1 f g x y = f (g x y) x
(with one caveat - partial application - that I suspect will only confuse matters further here).
In both cases, the declaration (left of the = in Haskell, and before the : in Python) only tells us the function name and how many arguments it takes.
In both cases, we can infer from the definition (right hand side in Haskell, the indented block after : in Python) that f and g are both functions on two arguments, that g's first argument is the same as f's second, etc. In Haskell, the compiler does this for us; Python will just complain at runtime if we call g with the wrong number of arguments, or it returns something f can't use as a first argument.