Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to determine type of Haskell functions?

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

like image 298
Soph Avatar asked Jul 24 '12 19:07

Soph


People also ask

How do you find the type of function in Haskell?

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.

What is the type of the Haskell function?

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 .

Does every Haskell function have a type?

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.

How does Haskell infer types?

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.


1 Answers

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.

like image 60
Useless Avatar answered Oct 02 '22 13:10

Useless