我实例头的思维模型是它们是类型级别的模式匹配,具有像变量绑定一样的tyvar绑定。使用FlexibleInstances
,您可以在实例头中重复使用tyvar。但是您不能在学期一级做到这一点。考虑
class (Eq a, Eq b) => C a b where
foo :: a -> b -> String
instance C a a where -- repeated `a'
-- the args are same type so we can compare them
foo ... = "equal" -- see below
foo _x _y = "not equal"
instance {-# OVERLAPPABLE #-} C a b where
foo _ _ = "not comparable"
对于这种"equal"
案例,我想写
foo x x = "equal"
但是模式中不允许重复的变量:拒绝的Conflicting definitions for 'x'
。这是因为每个变量必须唯一地绑定到foo
then 的参数上,我们才能引用它们。而是去
foo x y | x == y = "equal"
那么重复的tyvar业务如何运作呢?在instance C a a
中,哪个a
是绑定站点,哪个是引用站点?幕后的编译器在执行等同于绑定到不同名称的tyvar的操作,然后应用防护吗?
答案 0 :(得分:1)
实例选择通常在ghc/compiler/types/InstEnv.hs
中进行,它使用“匹配”,这是一种受限制的统一形式,其中带有变量的模板通过替换那些模板变量而与另一种类型统一。
因此,当GHC决定需要C Int Int
的实例字典时,它将尝试按以下顺序将C Int Int
与模板C a a
和C a b
进行匹配/统一(即,通过应用tcMatchTys
中的函数ghc/compiler/types/Unify.hs
来确定是否存在变量a
和/或b
的替代项,从而允许这些模板之一与{ {1}}。
这是C Int Int
中的一个实现细节,但是在我看来,tcMatchTys
可以通过将C Int Int
绑定到基于C a a
的模板而与模板a
相匹配。在第一个Int
上,然后根据第二个a
验证a
已绑定到Int
,因此我猜第一个a
是绑定站点,并且第二个a
是引用站点,尽管更准确地说它们都是统一/匹配站点。如果更改顺序,语义将不会改变。
重叠实例的处理在此匹配过程之后进行。生成匹配模板的完整列表,然后在a
中由insert_overlapping
折叠:
lookupInstEnv
函数-- in function `lookupInstEnv`, file `InstEnv.hs`
final_matches = foldr insert_overlapping [] all_matches
会在允许重叠的地方严格过滤掉更通用的实例(并处理不连贯的实例)。然后将最终列表与单例列表进行匹配,如果零个或多个模板匹配,则错误。
请注意,上面的匹配过程可能涉及多态替换,所以:
insert_overlapping
使用相同的匹配过程来确定gack :: [t] -> String
gack xs = foo xs xs
与替换项C [t] [t]
下的模板C a a
匹配。 (它在替换项a := [t]
下也匹配C a b
,但优势匹配获胜。)