I have stumbled upon lots of exercises that give you a function and ask for you to deduce the type of each one.
I have the following example. Please note this is not homework that I need to get done. I have the answer to this specific example and provide it below. Maybe someone can help me out in learning how to reason this kind of exercises.
The function:
h1 f g x y = f (g x y) x
The supposed type:
h1 :: (a -> b -> c) -> (b -> d -> a) -> b -> d -> c
Thanks!
I added 27 exercises here without solutions.
Some of them have solutions included here.
However, it is possible to know the type by using the GHCi command :t
So, taking the argument list from left-to-right:
fis a function applied to two arguments: the first is the result of(g x y)and the second isxabc), but we do know this must be the type returned byh1fis a function mappinga -> b -> cgis a function applied to two arguments: the first isxand the secondygis the same as the second argument tof, so it must be the same type: we already labelled thatbgisy, which we haven’t assigned a placeholder type yet, so that gets the next in sequence,dg‘s result is the first argument tof, and we already labelled thatagis a function mappingb -> d -> ax, and as that’s the second argument tof, we’ve already labelled its typeby, which is the second argument tog, so we’ve already labelled its typedh1is the result of applyingfto(g x y) x, as we said before, so it has the same type, already labelledcAlthough 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:
fis the first argument to consider, so let’s look at the body ofh1(everything after the=) to see how it’s usedf (g x y) xmeans thatfis applied to(g x y) x, sofmust 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 tofxis just a simple argument tof, passed straight fromh1‘s own argument listfis a function taking two argumentsIf it helps read
f (g x y) x, you can consider the equivalent expression in C-like notation would bef(g(x,y), x). Here, you can see right away thatfandgare functions taking two arguments, thatf‘s first argument is whatevergreturns, etc.Note that the left-hand side of the expression,
h1 f g x y, only gives one piece of type information by itself:h1is a function on four arguments. The argument names themselves are just placeholders used in the right-hand side of the expression (the body ofh1). The relative ordering of the arguments here just tells us how to callh1, but nothing about howh1uses the arguments internally.Again, here’s a procedural-style equivalent (I’ll use Python so I don’t have to fill in any types):
this means exactly the same as
(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) thatfandgare both functions on two arguments, thatg‘s first argument is the same asf‘s second, etc. In Haskell, the compiler does this for us; Python will just complain at runtime if we callgwith the wrong number of arguments, or it returns somethingfcan’t use as a first argument.