sexta-feira, 27 de junho de 2008

Rank 2 Types

I've always seen the forall a types in some GHC messages or in other person's code, but I couldn't get the point of it until I needed to use it myself. I was writing a Gtk2hs application with some windows, and I noticed that for each created window, I was doing the same steps. So I created a function that does what is needed for each function:

basic :: WidgetClass widget => String -> IO (Widget, (GObject -> Widget) -> String -> IO widget)
basic gladeFile =
  do
    (windowGlade :: GladeXML) <- getGlade gladeFile
    let
      windowGet :: Get
      windowGet = xmlGetWidget windowGlade
    (window :: Widget) <- windowGet castToWidget "window"
    (close  :: Button) <- windowGet castToButton "close"
    onClicked close $ widgetDestroy window
    modifyIORef windows (window :)
    return (window, windowGet)


Notice that this code uses GHC extension PatternSignatures, which I like a lot.

The problem was that the returned function, windowGet was not generalized enough, so I couldn't use it with more than one type, even it being very general:

windowGet :: WidgetClass widget => (GObject -> widget) -> String -> IO widget

If I used it with, say, windowGet castToButton "ok" and WindowGet castToSpinButton "value", it would give, in the seconde line, the type error: Couldn't match expected type `Button' against inferred type `SpinButton'.

After asking in #haskell, and reading a little bit of the GHC User's Guide, I got the point. This was only possible with Rank 2 Types. windowGet must be:

windowGet :: forall widget. WidgetClass widget => (GObject -> widget) -> String -> IO widget

So I changed the type signature for basic, and added Rank2Types to the LANGUAGE pragma, and it worked fine.

basic :: String -> IO (Widget, forall widget. WidgetClass widget => (GObject -> Widget) -> String -> IO widget)

Nenhum comentário: