GHC用户指南参考以下示例描述impredicative polymorphism extension:
f :: Maybe (forall a. [a] -> [a]) -> Maybe ([Int], [Char])
f (Just g) = Just (g [3], g "hello")
f Nothing = Nothing
但是,当我在文件中定义此示例并尝试调用它时,我收到类型错误:
ghci> f (Just reverse)
<interactive>:8:9:
Couldn't match expected type `forall a. [a] -> [a]'
with actual type `[a0] -> [a0]'
In the first argument of `Just', namely `reverse'
In the first argument of `f', namely `(Just reverse)'
In the expression: f (Just reverse)
ghci> f (Just id)
<interactive>:9:9:
Couldn't match expected type `forall a. [a] -> [a]'
with actual type `a0 -> a0'
In the first argument of `Just', namely `id'
In the first argument of `f', namely `(Just id)'
In the expression: f (Just id)
似乎只有undefined
,Nothing
或Just undefined
符合类型检查器。
因此我有两个问题:
Just f
为任何非底部f
调用上述功能?后者尤其考虑到HaskellWiki page on Impredicative Polymorphism,目前对于扩展的存在确实令人难以置信。
答案 0 :(得分:7)
是不是只有ImpredicativeTypes
在ghc-7 +中被新的类型检查器悄然丢弃了?请注意,ideone.com仍然使用ghc-6.8,实际上您的程序运行正常:
{-# OPTIONS -fglasgow-exts #-}
f :: Maybe (forall a. [a] -> [a]) -> Maybe ([Int], [Char])
f (Just g) = Just (g [3], g "hello")
f Nothing = Nothing
main = print $ f (Just reverse)
按预期打印Just ([3],"olleh")
;见http://ideone.com/KMASZy
augustss
提供了一个漂亮的用例 - 某种模仿Python dsl - 以及此处扩展的辩护:http://augustss.blogspot.com/2011/07/impredicative-polymorphism-use-case-in.html在此处的票证中提到http://hackage.haskell.org/trac/ghc/ticket/4295
答案 1 :(得分:7)
以下是一个项目const-math-ghc-plugin如何使用ImpredicativeTypes
指定匹配规则列表的示例。
我们的想法是,当我们有App (PrimOp nameStr) (Lit litVal)
形式的表达式时,我们希望根据primop名称查找适当的规则。 litVal
可以是MachFloat d
或MachDouble d
(d
是Rational
)。如果我们找到规则,我们希望将该规则的函数应用于d
转换为正确的类型。
函数mkUnaryCollapseIEEE
为一元函数执行此操作。
mkUnaryCollapseIEEE :: (forall a. RealFloat a => (a -> a))
-> Opts
-> CoreExpr
-> CoreM CoreExpr
mkUnaryCollapseIEEE fnE opts expr@(App f1 (App f2 (Lit lit)))
| isDHash f2, MachDouble d <- lit = e d mkDoubleLitDouble
| isFHash f2, MachFloat d <- lit = e d mkFloatLitFloat
where
e d = evalUnaryIEEE opts fnE f1 f2 d expr
第一个参数需要具有Rank-2类型,因为它将在Float
或Double
实例化,具体取决于文字构造函数。规则列表如下所示:
unarySubIEEE :: String -> (forall a. RealFloat a => a -> a) -> CMSub
unarySubIEEE nm fn = CMSub nm (mkUnaryCollapseIEEE fn)
subs =
[ unarySubIEEE "GHC.Float.exp" exp
, unarySubIEEE "GHC.Float.log" log
, unarySubIEEE "GHC.Float.sqrt" sqrt
-- lines omitted
, unarySubIEEE "GHC.Float.atanh" atanh
]
这很好,如果我的口味有点太多样板。
但是,有一个类似的功能mkUnaryCollapsePrimIEEE
。在这种情况下,不同GHC版本的规则是不同的。如果我们想支持多个GHC,那就有点棘手了。如果我们采用相同的方法,subs
定义将需要大量的CPP,这可能是不可维护的。相反,我们在每个GHC版本的单独文件中定义规则。但是,由于循环导入问题,mkUnaryCollapsePrimIEEE
在这些模块中不可用。我们可能会重新构建模块以使其工作,但我们将规则集定义为:
unaryPrimRules :: [(String, (forall a. RealFloat a => a -> a))]
unaryPrimRules =
[ ("GHC.Prim.expDouble#" , exp)
, ("GHC.Prim.logDouble#" , log)
-- lines omitted
, ("GHC.Prim.expFloat#" , exp)
, ("GHC.Prim.logFloat#" , log)
]
通过使用ImpredicativeTypes
,我们可以保留Rank-2函数列表,准备用于mkUnaryCollapsePrimIEEE
的第一个参数。替代方案将是CPP /样板,更改模块结构(或循环导入)或大量代码重复。我不想要这些。
我似乎回忆起GHC HQ表示他们希望放弃对扩展的支持,但也许他们已经重新考虑了。它有时非常有用。
答案 2 :(得分:5)
请注意此解决方法:
justForF :: (forall a. [a] -> [a]) -> Maybe (forall a. [a] -> [a])
justForF = Just
ghci> f (justForF reverse)
Just ([3],"olleh")
或者这个(内联的内容基本相同):
ghci> f $ (Just :: (forall a. [a] -> [a]) -> Maybe (forall a. [a] -> [a])) reverse
Just ([3],"olleh")
似乎类型推断在您的案例中推断Just
的类型时遇到问题,我们必须告诉它类型。
我不知道这是一个错误还是有充分的理由......:)