Adventures in Haskell – Type inference

I’m learning Haskell, so I thought I’d write about my adventures as I go along.

One of the first things I noticed about Haskell (apart from the obvious, such as being functional) is that it is very good about type inference. This means that the vast majority of the time, I don’t need to tell Haskell what types to use for my functions (although it’s good style), and I don’t have to tell it what types to use for my variables. For example, consider this simple, inefficient function for calculating the Fibonacci sequence:

fib 0 = 1
fib 1 = 1
fib n = fib (n - 1) + fib (n - 2)

Haskell was able to automatically infer that the function has the type (Num t, Num t1) => t -> t1, which means it takes a number and returns a number. Even though I didn’t declare any types, it’s still strongly typed; if I try to execute fib "1", I get the error “No instance for (Num [Char])“, which is Haskell’s way of saying that a string (a list of characters) is not a number.

As I said, it’s good practice to declare the types of functions. In this case, our code should look like this:

fib :: (Integral t) => t -> t
fib 0 = 1
fib 1 = 1
fib n = fib (n - 1) + fib (n - 2)

This tells it the input should be an Integral type (either Int or Integer, equivalent to Java’s int or BigInteger). I’ve restricted it because, while passing it 10.0 is fine, passing it 10.1 will not terminate. Passing it -1 will also not terminate, but that’s a separate issue. It also says that the output type is the same as the input type, unlike the inferred types, where the input may be an Int while the output’s an Integer. Notice how there’s only one type variable t instead of t and t1.

There are cases where type inference fails. One example is passing the output of a function that can return anything into a function that can accept anything. Specifically, print (read "1") will fail to compile with the error “Ambiguous type variable `a' in the constraints“. The type for read is (Read a) => String -> a, and the type for print is (Show a) => a -> IO (). The type variable a can be matched with any type that has an instance for Show and Read (implements the interfaces, in Java terminology). Since many will fit, the compiler does not not which one to use. In this case, we have to tell it like so: print (read "1" :: Int).

Post a Comment

Your email is never shared. Required fields are marked *

*
*