GHC重写规则专门为一个类型类的函数

使用GHC RULES编译指示,可以为特定类型专门设计一个多态函数。 来自Haskell报告的例子:

genericLookup :: Ord a => Table a b   -> a   -> b
intLookup     ::          Table Int b -> Int -> b

{-# RULES "genericLookup/Int" genericLookup = intLookup #-}

这将使GHC在整数索引表上使用intLookup ,否则在泛型上使用intLookup ,其中intLookup可能更有效。

我想用类似下面的(稍微简化的)函数来完成类似的事情:

lookup    :: Eq a  => [(a, b)] -> a -> b
lookupOrd :: Ord a => [(a, b)] -> a -> b

其中lookupOrd创建Map从输入列表,然后使用Map.lookup ,这就要求a是成员Ord

现在我想告诉GHC,只要a确实是Ord类型的成员, lookupOrd应该使用lookupOrd而不是lookup 。 但是,以下规则不会检查:

{-# RULES "lookup/Ord" lookup = lookupOrd #-}

GHC(理所当然地)抱怨说它不能从上下文(Eq a)推出(Ord a) (Eq a) 。 是否有一个重写规则可以让我执行这种基于类的专业化?


我认为不存在,并且有一个原因,为什么GHC目前的实施难以实现:

虽然您在Haskell语法中指定规则,但它们将应用于GHC的Core语言。 在那里,类型的类约束已经变成了字典参数,所以函数

lookup :: Eq a => a -> [(a,b)] -> Maybe b

现在已经打字

lookup :: forall a b. Eq a -> a -> [(a, b)] -> Maybe b

而你的lookupOrd会有类型

lookupOrd :: forall a b. Ord a -> a -> [(a, b)] -> Maybe b

其中, Eq aOrd a已经成为普通的数据类型。 特别是在这个阶段,不再有类型的类型的概念; 所有已经解决的问题。

所以现在假设编译器发现了一个

lookup (dict :: Eq MyType) (x :: MyType) (list :: [(MyType, String)])

它应该取代什么? 你告诉他, xlist也可以传递给lookupOrd ,但是该函数也需要Ord MyType类型的值,该值不会出现在规则的左侧。 所以GHC必须放弃。

像一条规则

{-# RULES "lookup/Ord" forall a x. lookup a x = lookupOrd (a::()) x #-}

尽管如此,这里有问题的参数( Ord字典)已经在规则中修复,并且在应用规则时不需要找到。

原则上,其他编译器设计可能允许您想要的表单规则。


虽然这是一个古老的问题,但未来的Google人可能有兴趣知道有一种方法可以使用ifcxt软件包来执行OP所需的操作。

您可以查看更多示例的文档,但基本上可以使用第二个示例,示例2:使代码渐近有效,作为基础。

有了这两个功能,

lookup    :: Eq a  => [(a, b)] -> a -> b
lookupOrd :: Ord a => [(a, b)] -> a -> b

你会做,

cxtLookup :: forall a. (Eq a, IfCxt (Ord a)) => [(a, b)] -> a -> b
cxtLookup = ifCxt (Proxy::Proxy (Ord a)) lookupOrd lookup

这将允许您根据数据结构实现的类型特性选择最有效的函数。

请注意,我不知道它会对性能产生多大影响,但我认为与查找函数的运行时间相比,它是微不足道的,因此确实值得。

链接地址: http://www.djcxy.com/p/43065.html

上一篇: GHC rewrite rule specialising a function for a type class

下一篇: derived `Eq` instance really *O(N)*?