我们说我有以下功能:
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"
我能做些什么吗?
答案 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)
最有可能发生这种情况,因为widget
与castToWindow
的具体选择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!
由于我们无法统一Int
和Double
而导致错误,但必须输入(+1)
。如果我们明确要求typechecker延迟实例化(+1),那么
poly :: (forall n . Num n => n -> n) -> (Int, Double)
poly f = (f 1, f 1.)
>>> poly (+1)
(2, 2.0)
我们没事。