我正在研究一个monadic流媒体库,我遇到了一个我不理解的类型。我已设法将其缩小为以下示例:
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeFamilies #-}
class Foo a b where
type E a b :: *
(>->) :: a -> b -> E a b
data Bar x
instance Foo (Bar x) (Bar x) where
type E (Bar x) (Bar x) = Bar x
(>->) = undefined
x = undefined :: Bar a
y = undefined :: Bar Int
z = x >-> y
当我尝试编译它时,我得到:
No instance for (Foo (Bar a0) (Bar Int))
arising from a use of ‘>->’
The type variable ‘a0’ is ambiguous
Relevant bindings include
z :: E (Bar a0) (Bar Int)
(bound at /usr/local/google/home/itaiz/test/test.hs:17:1)
Note: there is a potential instance available:
instance Foo (Bar x) (Bar x)
-- Defined at /usr/local/google/home/itaiz/test/test.hs:10:10
In the expression: x >-> y
In an equation for ‘z’: z = x >-> y
我猜,这让我感到惊讶,不过可能不会太多。让我感到惊讶的是,如果我用以下内容替换实例,那么一切正常:
instance (x ~ x') => Foo (Bar x) (Bar x') where
type E (Bar x) (Bar x') = Bar x
(>->) = undefined
我没有看到两个实例声明之间的区别。我猜这与类型变量的范围有关。有人可以解释发生了什么吗?
[旁白:我在使用fundeps时也会看到同样的事情。]
答案 0 :(得分:5)
修改:GHC user guide section on instance resolution是一个很好的起点。
以下是如何分解为什么会这样。您的z
大致相当于:
z :: Bar a -> Bar Int -> E (Bar a) (Bar Int)
z = (>->)
现在更清楚为什么不可能?我们得到的错误是:
SO26146983.hs:20:5:
No instance for (Foo (Bar a) (Bar Int)) arising from a use of `>->'
In the expression: (>->)
In an equation for `z': z = (>->)
没有任何迹象表明a ~ Int
。让我们改写它:
z' :: (a ~ Int) => Bar a -> Bar Int -> E (Bar a) (Bar Int)
z' = (>->)
即使您的原始实例也可以正常工作。 (编辑:我怀疑以下句子无益或误导或两者兼而有之。)z'
(大致)是typechecker以重写的实例定义结束的地方:它看到{的实例{1}}需要(Bar a) (Bar a')
,只需将该约束添加到调用中。
粗略地说,实例解析从右到左,有时会产生意想不到的后果。
修改:从右到左分辨率的结果是(a ~ a')
匹配任何两种类型instance (x ~ x') => Foo (Bar x) (Bar x')
和{{1无论x
是否确实如此。约束仅传播到调用站点。因此,您无法为特定类型编写另一个实例。它会重叠,默认情况下是禁止的,而且GHC在解析实例时特别不会回溯。另一方面,x'
将不会被应用,除非它在两个地方都是相同的类型 - GHC不会发明约束,因为x ~ x'
与instance Foo (Bar x) (Bar x)
不同。< / p>
根据您的实际用例,您可能需要阅读OverlappingInstances的文档。同样取决于你正在做什么,type families中的一些最新创新,例如closed type families,可能是相关的。