Functional dependencies in Haskell

When you have a multiparameter typeclass, by default, the type variables are considered independently. So when the type inferencer is trying to figure out which instance of

class Foo a b

to choose, it has to determine a and b independently, then go look check to see if the instance exists. With functional dependencies, we can cut this search down. When we do something like

class Foo a b | a -> b

We’re saying “Look, if you determine what a is, then there is a unique b so that Foo a b exists so don’t bother trying to infer b, just go look up the instance and typecheck that”. This let’s the type inferencer by much more effective and helps inference in a number of places.

This is particularly helpful with return type polymorphism, for example

class Foo a b c where
  bar :: a -> b -> c

Now there’s no way to infer

  bar (bar "foo" 'c') 1

Because we have no way of determining c. Even if we only wrote one instance for String and Char, we have to assume that someone might/will come along and add another instance later on. Without fundeps we’d have to actually specify the return type, which is annoying. However we could write

class Foo a b c | a b -> c where
  bar :: a -> b -> c

And now it’s easy to see that the return type of bar "foo" 'c' is unique and thus inferable.

Leave a Comment