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)