使用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)
。是否有重写规则允许我执行这种类型的基于类的特化?
答案 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 a
和Ord a
已成为普通数据类型。特别是,在这个阶段,对于某个类型, 类型类概念不再存在;所有这些都已经解决了。
现在假设编译器发现
的出现lookup (dict :: Eq MyType) (x :: MyType) (list :: [(MyType, String)])
它应该用什么代替?你告诉他x
和list
也可以传递给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
这将允许您根据数据结构实现的类型类选择最有效的函数。
请注意,我不知道它会对性能产生多大影响,但我认为与查找函数的运行时相比,这是微不足道的,因此确实值得。