考虑以下代码,它会进行类型检查:
module Scratch where
import GHC.Exts
ensure :: forall c x. c => x -> x
ensure = id
type Eq2 t = (forall x y. (Eq x, Eq y) => Eq (t x y) :: Constraint)
foo :: forall t a.
( Eq2 t
, Eq a
) => ()
foo = ensure @(Eq (a `t` a)) ()
foo
在这里没有做任何有用的事情,但是让我们想象它正在做一些需要Eq (t a a)
实例的重要业务。编译器能够接受(Eq2 t, Eq a)
约束并详细说明Eq (t a a)
字典,因此约束被解除并且一切正常。
现在假设我们想要foo
做一些额外的工作,这些工作依赖于以下相当复杂的类的实例:
-- some class
class (forall ob x y. (SomeConstraint t ~ ob, ob x, ob y) => ob (t x y)) =>
SomeClass t
where
type SomeConstraint t :: * -> Constraint
foo' :: forall t a.
( Eq2 t
, Eq a
, SomeClass t -- <- the extra constraint
) => ()
foo' = ensure @(Eq (a `t` a)) ()
请注意,在我们的主体中,foo'
我们仍然只要求我们在 中所做的foo
:一个Eq (t a a)
约束。此外,我们没有删除或修改编译器用于详细说明 in 实例的Eq (t a a)
约束foo
;我们仍然要求(Eq2 t, Eq a)
除了新的约束。所以我也希望foo'
进行类型检查。
不幸的是,看起来实际发生的是编译器忘记了如何详细说明Eq (t a a)
. 这是我们在正文中得到的错误foo'
:
• Could not deduce (Eq (t a a)) arising from a use of ‘ensure’
from the context: (Eq2 t, Eq a, SomeClass t)
bound by the type signature for:
foo' :: forall (t :: * -> * -> *) a.
(Eq2 t, Eq a, SomeClass t) =>
()
鉴于编译器可以“Eq (t a a)
从上下文”很好地“推断” (Eq2 t, Eq a)
,我不明白为什么更丰富的上下文(Eq2 t, Eq a, SomeClass t)
会导致Eq (t a a)
不可用。
这是一个错误,还是我只是做错了什么?在这两种情况下,是否有一些解决方法?