TypeFamilies vs. FunctionalDependencies & type-level recursion

dm-list-haskell-prime at scs.stanford.edu dm-list-haskell-prime at scs.stanford.edu
Tue Jun 14 19:19:44 CEST 2011

At Tue, 14 Jun 2011 12:31:47 -0400,
Dan Doel wrote:
> Sorry about the double send, David. I forgot to switch to reply-all in
> the gmail interface.
> Okay. I don't really write a lot of code like this, so maybe I missed
> the quirks.
> In that case, HList has been relying on broken behavior of fundeps for
> 7 years. :) Because the instance:
>    instance TypeEq a b c
> violates the functional dependency, by declaring:
>    instance TypeEq Int Int Int
>    instance TypeEq Int Int Char
>    instance TypeEq Int Int Double

No, these are not equivalent.  The first one "TypeEq a b c" is just
declaring an instance that works "forall c".  The second is declaring
multiple instances, which, if there were class methods, could have
different code.  The second one is illegal, because given just the
first two types, a and b, you cannot tell which instance to pick.

>    class C a b | a -> b where
>      foo :: a -> b
>      foo = error "Yo dawg."
>    instance C a b where
>    bar :: Int
>    bar = foo "x"
>    baz :: Char
>    baz = foo "x"
> so we're using an instance C String Int and an instance C String Char
> despite the fact that there's a functional dependency from the first
> argument to the second.

A functional dependency such as "| a b -> c d" just guarantees that a
and b uniquely determine the instance.  Hence, it is okay to have
class methods that do not mention type variables c and d, because the
compiler will still know which instance to pick.

Since your code only has one instance of class C, the unique instance
is trivially guaranteed and the code is fine.  In fact, your code is
effectively the same as:

	class C a where
	  foo :: a -> b
	  foo = error "Yo dawg."

	instance C a where

The same issues happen with type families.  The following code is
obviously illegal, because you have a duplicate instance of class C:

	class C a where
	    type Foo a
	    foo :: a -> Foo a
	    foo = error "Yo dawg."
	instance C [Char] where
	    type Foo [Char] = Int
	instance C [Char] where
	    type Foo [Char] = Char

On the other hand, though the compiler won't accept it, you could at
least theoretically allow code such as the following:

	instance C [Char] where
	    type Foo [Char] = forall b. () => b


More information about the Haskell-prime mailing list