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
If you need to figure out what the type of an object is in a Haskell program, I hope this is helpful. Note that if you are in GHCI, you can just put :type before your expression to determine the expression's type, or use :set +t to see the type of every expression in GHCI.
Haskell has first-class functions : functions are values just like integers, lists, etc. They can be passed as arguments, assigned names, etc. … val is value of type Int , and half_of is a value of type Float -> Float .
Everything in Haskell has a type, so the compiler can reason quite a lot about your program before compiling it. Unlike Java or Pascal, Haskell has type inference. If we write a number, we don't have to tell Haskell it's a number.
Types are infered using a process generally called unification. Haskell belongs to the Hindley-Milner family, which is the unification algorithm it uses to determine the type of an expression. If unification fails, then the expression is a type error.
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
a
b
c
), but we do know this must be the type returned by h1
f
is a function mapping a -> b -> c
g
is a function applied to two arguments: the first is x
and the second y
g
is the same as the second argument to f
, so it must be the same type: we already labelled that b
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
g
is a function mapping b -> d -> a
x
, and as that's the second argument to f
, we've already labelled its type b
y
, which is the second argument to g
, so we've already labelled its type d
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 listf
is a function taking two argumentsIf 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With