Overlapping Instances in Haskell

September 27, 2008

In an earlier post I proposed adding composition to wxHaskell. In a effort to make the composites seem as much like wxHaskell widgets as possible, some type classes were instantiated automatically and some could be “inherited” with a little extra work. Unfortunately, the latter kind proved to be problematic. They are given this type of instance declarations:

instance Checkable super => Checkable (Composite super user) where
    checkable = mapFromSuper checkable
    checked   = mapFromSuper checked

which says that if the super-type (see blogpost for details) implements Checkable, then so do the composite.

But what if you do not want the default implementation of Checkable, but want to roll your own? You are our of luck, as Haskell compilers will complain about overlapping instances. Even if the super-type do not implement Checkable, then compilers will still complain as contexts (everything before “=>” in the example above) will not influence the judgment of whether instances are overlapping. This fact is also described in the GHC manual:

When matching, GHC takes no account of the context of the instance declaration (context1 etc). GHC’s default behaviour is that exactly one instance must match the constraint it is trying to resolve.

This should not have been a surprise for me. Especially, as I have made a similar mistake before. Nonetheless, I somehow find it intuitive that a Haskell compiler would take contexts into consideration when deciding type classes, but they do not.

As a sidenote, I think the terms “inherited”, “super” and “user” was properly not a wice choice of words. But I made it in the original proposal, and I will keep it for now to avoid further confusion.

Solution to our Predicament

To avoid the problem described above, we have decided to remove the super type from the Composite type, which leaves just one type variable for the Composite type. To still handle inheritance of type classes we introduce a new type:

newtype Inherit super = Inherit { inherit :: super }

and instances like:

instance Checkable super => Checkable (Composite (Inherit super)) where
    checkable = mapInheritedAttr checkable
    checked   = mapInheritedAttr checked

mapInheritedEvent :: Event super event -> Event (CompositeInherit super) event
mapInheritedEvent event = mapEventF (inherit . pickUser) event

That is, we use the Inherit type as a marker type, like Java uses marker interfaces, to indicate that we want to inherit (or automatically derive) additional instances.

In this way, we avoid overlapping interfaces, keep the existing flexibility and even makes life a little simpler for people not inheriting instances, as they must now deal with only one type parameter to the Composite type.

You can download the new and improved WxGeneric here.

Leave a comment