Search code examples
haskelltypestype-constraints

Constraints in explicit signatures for monadic bindings


Let's say I have the following function:

loadDialog :: String -> IO MyDialog
loadDialog file = do
  Just ui <- xmlNew file
  MyDialog
    <$> xmlGetWidget ui castToWindow "w"
    <*> xmlGetWidget ui castToButton "b"

where

xmlGetWidget :: WidgetClass widget => GladeXML -> (GObject -> widget) -> String -> IO widget

Now I want to capture the following xmlNew/xmlGetWidget usage pattern:

widgetBinder :: WidgetClass widget => FilePath -> IO ((GObject -> widget) -> String -> IO widget)
widgetBinder file = do
  Just ui <- xmlNew file
  return $ xmlGetWidget ui

Which should allow me to write:

loadDialog file = do
  bind <- widgetBinder file
  MyDialog
    <$> bind castToWindow "w"
    <*> bind castToButton "b"

The problem is, it doesn't typecheck (exact error here). I've thought it's possible to provide generic signatures to bindings explicitly, but it seems this is not the case for monadic bindings since the following doesn't typecheck as well (even with RankNTypes, error here):

loadDialog file = do
  bind :: WidgetClass w => (GObject -> w) -> String -> IO w
       <- widgetBinder file
  MyDialog
    <$> bind castToWindow "w"
    <*> bind castToButton "b"

Is there anything I can do?


Solution

  • A clunky-but-workable solution is to throw your function into a newtype:

    newtype Binder = Binder (forall w. WidgetClass w => (GObject -> w) -> String -> IO w)
    
    widgetBinder :: FilePath -> IO Binder
    widgetBinder file = do
      Just ui <- xmlNew file
      return $ Binder (xmlGetWidget ui)
    
    loadDialog file = do
      Binder bind <- widgetBinder file
      MyDialog
        <$> bind castToWindow "w"
        <*> bind castToButton "b"
    

    Or something like that...