monadic绑定的显式签名中的约束

时间:2013-12-27 15:45:46

标签: haskell types type-constraints

我们说我有以下功能:

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

,其中

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

现在我想捕获以下xmlNew / xmlGetWidget使用模式:

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

哪个应该允许我写:

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

问题是,它没有进行类型检查(确切错误here)。我认为可以明确地为绑定提供通用签名,但似乎这不是monadic绑定的情况,因为以下也没有进行类型检查(即使使用RankNTypes,错误{{ 3}}):

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

我能做些什么吗?

2 个答案:

答案 0 :(得分:3)

一个笨重但可行的解决方案是将你的函数放入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"

或类似的......

答案 1 :(得分:2)

最有可能发生这种情况,因为widgetcastToWindow的具体选择castToButton不同。当类型检查器尝试确定bind的类型时,它会尝试在两个设置中使用有关其应用程序的信息,并看到它们发生冲突。换句话说,多态性太少了。

为避免这种情况,您需要一个明确的签名和RankNTypes,就像您尝试过的那样。

loadDialogue' :: (forall w. -> WidgetClass w => (GObject -> w) -> String -> IO w)
              -> IO MyDialogue
loadDialogue' bind = MyDialogue
                       <$> bind castToWindow "w"
                       <*> bind castToButton "b"

loadDialogue :: String -> IO MyDialogue
loadDialogue file = widgetBinder file >>= loadDialogue'

请注意,输入函数本地包含的forall可确保以多态方式定义该函数,从而在每个站点单独实例化。

作为一个更简单的例子,我们可以尝试(并且失败)来定义poly

poly :: (Int, Double)
poly = let f = (+1)
       in (f 1, f 1.0)         -- typecheck fail!

由于我们无法统一IntDouble而导致错误,但必须输入(+1)。如果我们明确要求typechecker延迟实例化(+1),那么

poly :: (forall n . Num n => n -> n) -> (Int, Double)
poly f = (f 1, f 1.)

>>> poly (+1)
(2, 2.0)

我们没事。