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

is a function applied to two arguments: the first is the result of and the second is 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

is a function applied to two arguments: the first is and the second third argument is x , and as that's the second argument to f , we've already labelled its type b

, and as that's the second argument to , we've already labelled its type fourth argument is y , which is the second argument to g , so we've already labelled its type d

, which is the second argument to , so we've already labelled its type 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

is the first argument to consider, so let's look at the body of (everything after the ) to see how it's used

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.