Domanda

I am struggling to get the following code past GHC:

getFirstChild :: (WidgetClass w1, WidgetClass w2) => w1 -> IO (Maybe w2)
getFirstChild parent = do
   -- check if parent is a container
   if parent `isA` gTypeContainer
      -- if parent is a container get the first child
      then do children <- containerGetChildren $! castToContainer parent
              return $! Just $! children !! 0
      else return Nothing

Even though at first glance it looks like a Gtk2hs question, it is really about the Haskell type system.

When I try to compile this code with GHC, I get the following error message:

Could not deduce (w2 ~ Widget)
from the context (WidgetClass w1, WidgetClass w2)
  bound by the type signature for
             getFirstChild :: (WidgetClass w1, WidgetClass w2) =>
                              w1 -> IO (Maybe w2)
  at HsFu\Gtk\Widget.hs:(6,4)-(12,28)
  `w2' is a rigid type variable bound by
       the type signature for
         getFirstChild :: (WidgetClass w1, WidgetClass w2) =>
                          w1 -> IO (Maybe w2)
       at HsFu\Gtk\Widget.hs:6:4
Expected type: [w2]
  Actual type: [Widget]
In the first argument of `(!!)', namely `children'
In the second argument of `($!)', namely `children !! 0'

The type of containerGetChildren is:

containerGetChildren :: ContainerClass self => self -> IO [Widget]

The Widget type is itself an instance of WidgetClass, so I do not understand why I cannot have the return type of the getFirstChild function specified as w2 that is an instance of WidgetClass.

Is there a way to express this in Haskell without using things like unsafeCoerce ?

TIA

È stato utile?

Soluzione

No, there isn't a way. Your declraration

getFirstChild :: (WidgetClass w1, WidgetClass w2) => w1 -> IO (Maybe w2)

says that your function can return any w2 as long as it's in WidgetClass, but that's a lie, because it only returns Widgets. Haskell won't let you mislead the programmer like this.

Suppose that I were importing your module and wrote

data MyWodget = ....
instance WidgetClass MyWodget where ....

Then it would be reasonable for me to expect from your type signature that your function could return a Maybe MyWodget as long as I'd put some MyWodgets inside another MyWodget, but because the function you've used in your definition can only work with Widgets, Haskell can't use it on a MyWodget, and my code would both typecheck and not typecheck.

You won't be able to fix this with unsafeCoerce because a MyWodget simply isn't a Widget in any way; haskell typeclasses let you use truly different data types with the same functional interface, so you can't just coerce there.

You could define your own class

class FromWidget w where -- not a good name, I admit
    fromWidget :: Widget -> w

Then you could use fromWidget to rewrite your function to be more general:

getFirstChild :: (WidgetClass w1, FromWidget w2) => w1 -> IO (Maybe w2)

and I could use it on MyWodget as long as I make

instance FromWidget MyWodget where ....

But I deeply doubt that there's a way of making everything in WidgetClass this way. Maybe you can make every WidgetClass thing you're interested in, but maybe you should check if you're actually only interested in Widgets anyway.

Altri suggerimenti

According to the type of your function your function should work for any w1 and w2 belonging to WidgetClass. But you are returning a more specific type Widget. Whenever you are in such kind of doubts, don't provide the type to the function and see what type ghci infers. ghci will always infer the more general type.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top