[Haskell-cafe] typeclass and functional dependency problem

2012-01-10 Thread Martin DeMello
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.

martin

___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] typeclass and functional dependency problem

2012-01-10 Thread John Lato
 From: Martin DeMello martindeme...@gmail.com
 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