GHC重写规则专门用于类型类的函数

时间:2013-11-02 18:03:47

标签: haskell ghc

使用GHC RULES pragma,可以为特定类型专门化多态函数。 Haskell报告中的示例:

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

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

这会使GHC在整数索引表上使用intLookup,否则使用通用版本,其中intLookup可能更有效。

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

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

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

现在我想告诉GHC,只要lookupOrd确实是lookup类型类的成员,就应该使用a代替Ord。但是,以下规则并未进行类型检查:

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

GHC(理所当然地)抱怨它不能从上下文(Ord a)中推断(Eq a)。是否有重写规则允许我执行这种类型的基于类的特化?

2 个答案:

答案 0 :(得分:13)

我认为没有,并且有理由说明GHC目前的实施并不容易实现:

虽然您在Haskell语法中指定了规则,但它们将应用于GHC的核心语言。在那里,类型类约束已经转换为字典参数,因此函数

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字典)已在规则中修复,在应用规则时不需要找到。

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

答案 1 :(得分:1)

虽然这是一个老问题,但未来的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

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

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