> From: Martin DeMello
> Subject: [Haskell-cafe] typeclass and functional dependency problem
>
> I'm writing a Gtk2hs app, and I have several custom widgets that are
> composite objects represented by records, one field of which is a
> container widget. I am trying to write a replacement for gtk2hs's
> boxPackStart
>
> boxPackStart :: (BoxClass self, WidgetClass child) => self -> child ->
> Packing -> Int -> IO ()
>
> that will accept either a gtk widget or one of my custom widgets to
> place in the box, and do the right thing. Here's my attempt at it;
> what I want to know is why the commented out bit didn't work and I had
> to individually add instances of widgets instead:
>
> -
> -- packable objects
> class WidgetClass w => Packable a w | a -> w where
> widgetOf :: a -> w
>
> --instance WidgetClass w => Packable w w where
> -- widgetOf = id
>
> instance Packable Button Button where
> widgetOf = id
>
> instance Packable Entry Entry where
> widgetOf = id
>
> instance Packable Label Label where
> widgetOf = id
>
> instance Packable Notebook Notebook where
> widgetOf = id
>
> instance Packable HBox HBox where
> widgetOf = id
>
> -- add widget to box
> boxPackS :: (BoxClass b, WidgetClass w, Packable a w) => b -> a ->
> Packing -> Int -> IO ()
> boxPackS box child p i = boxPackStart box (widgetOf child) p i
>
> -
>
> If I try to use
>
> instance WidgetClass w => Packable w w where
> widgetOf = id
>
> instead, I get the compilation error
>
> Editor.hs:23:10:
> Functional dependencies conflict between instance declarations:
> instance Packable PairBox VBox -- Defined at Editor.hs:23:10-30
> instance WidgetClass w => Packable w w
> -- Defined at GuiUtils.hs:13:10-38
>
> even though PairBox does not belong to WidgetClass.
This is a very common problem. The line
> instance WidgetClass w => Packable w w where
means "Every type is an instance of Packable by this declaration", not
"Every type that has a WidgetClass instance is a Packable by this
declaration", which is what you wanted. So you end up with two
Packable instances for PairBox, "Packable PairBox PairBox" and
"Packable PairBox Container" (or whatever type you used), which causes
this conflict.
This is a consequence of Haskell type classes being open. Even if you
don't have a WidgetClass instance for a type (PairBox) in scope where
you define this instance, one could be defined elsewhere and be in
scope at a call site.
Unfortunately Haskell doesn't provide a way to write what you want. I
can think of a few solutions, none of which are ideal:
1. Create separate instances for each type. Writing some generating
code to do this is probably the best available option.
2. Convert everything to a Container.
3. Make boxPackS a TH splice, and use a naming convention to
differentiate your objects from the built-in widgets.
I don't like 3) because it's fragile. Pretty much everything else is
a variant of either 1 (lots of instance decls) or 2 (another function
call/separate functions).
John L.
___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe