由于我不确定我是否理解这个问题,所以我将讨论您编写的代码;也许我漫无边际的某些部分会为您指明一个有用的方向,或者引发一些我可能回答的尖锐问题。也就是说:警告!前面漫无边际的不回答!
首先,让我们谈谈Bar班级。
-- class (i ~ S b) => Bar b i where
-- type S b
-- ...
既然我们知道约束i ~ S b,我们不妨放弃这个i论点,我将在接下来的讨论中这样做。
class Bar b where type S b
-- or, if the class is empty, just
-- type family S b
-- with no class declaration at all
下面是这个新类的实例的外观:
instance Bar (Z i) where type S (Z i) = i
instance Bar (Z' i) where type S (Z' i) = i
如果这对于任何类型的构造函数都是正确的,那么您可以考虑将其写为一个实例:
-- instance Bar (f i) where type S (f i) = i
现在,让我们谈谈Foo班级。要将其更改为与上述匹配,我们将编写
class Bar (T a) => Foo a where type T a
您已经声明了两个实例:
-- instance (Bar (Z i) i) => Foo (X (Z i) i) where
-- type T (X (Z i) i) = Z i
--
-- instance (Bar (Z' i) i) => Foo (X' (Z' i) i) where
-- type T (X (Z' i) i) = Z' i
我们可以Bar像以前一样去掉第二个参数,但我们也可以做另一件事:因为我们知道有一个Bar (Z i)实例(我们在上面声明了它!),我们可以去掉实例约束。
instance Foo (X (Z i) i) where type T (X (Z i) i) = Z i
instance Foo (X (Z' i) i) where type T (X (Z' i) i) = Z' i
您是否要保留i论点X取决于X应该代表什么。到目前为止,我们还没有改变任何类声明或数据类型的语义——只改变了它们的声明和实例化方式。更改X可能不具有相同的属性;不看定义很难说X。有了数据声明和足够多的扩展,上面的前奏就可以编译了。
现在,您的抱怨:
您说以下内容无法编译:
class Qux a
instance Foo (X a' Int) => Qux (X a' Int)
instance Foo (X' a' Int) => Qux (X' a' Int)
但是,使用UndecidableInstances,它确实在这里编译。它需要它是有道理的UndecidableInstances:没有什么可以阻止某人稍后出现并声明一个像这样的实例
instance Qux (X Y Int) => Foo (X Y Int)
Then, checking whether `Qux (X Y Int)` had an instance would require checking whether `Foo (X Y Int)` had an instance and vice versa. Loop.
你说,“它也需要实例约束S (T (X a'))) ~ Int,尽管我知道在我的程序中,这些总是同义词。”。我不知道第一个“它”是什么——我无法重现这个错误——所以我不能很好地回答这个问题。另外,正如我之前抱怨的那样,这个约束是不友好的:X :: (* -> *) -> * -> *, 因此X a' :: * -> *, 并T期望一个 kind 的论点*。所以我假设你的意思是S (T (X a' Int)) ~ Int。
尽管有这些抱怨,但我们可以问为什么S (T (X a' Int)) ~ Int不能从我们迄今为止的假设中得到证明。到目前为止,我们只Foo为符合模式的类型声明了实例,X (Z i) i并且X (Z' i) i. 因此,类型函数T只有在其参数类型符合其中一种模式时才能减少。该类型X a' Int不太适合这两种模式。我们可以把它塞进正确的模式:我们可以尝试减少 withX (Z Int) Int代替(比如说)。然后我们会发现T (X (Z Int) Int) ~ Z Int,因此S (T (X (Z Int) Int) ~ S (Z Int) ~ Int。
这回答了如何修复类型级别的减少,但没有解释如何修复任何未构建的代码。为此,我们必须找出类型检查器需要从S (T (X a' Int))to强制转换的原因Int,并看看我们是否可以说服它更具体(和可满足)的强制转换,例如S (T (X (Z Int) Int)) ~ Int,或者使用Bar上面建议的通用实例,S (T (X (f Int) Int)) ~ Int。如果没有足够的代码来重现您的错误,我们当然无法帮助您。
你问,“我能否让 Haskell 意识到,当我将 'Int' 作为 X 的第二个参数时,这与同义词 S (T (X a' Int)) (完全相同)是一样的?”。我完全不明白这个问题。您想以某种方式获得可证明的平等X a Int ~ S (T (X a' Int))吗?这就是我从你的问题的字面阅读中得到的解释。
在上下文中,我想您可能想问您是否可以获得可证明的平等b ~ S (T (X a b));在这种情况下,答案是“绝对!”。我们将滥用我们在上面知道的事实b ~ S (Z b)来将这个方程简化为更强的方程Z b ~ T (X a b)。然后我们可以将Foo上面的实例替换为做出此声明的实例,仅此而已:
instance Foo (X a b) where T (X a b) = Z b
或者,我们可以假设另一个合理的方程,T (X a b) ~ a; 然后,为了证明S (T (X a b)) ~ b我们只需要证明S a ~ b(通过减少T),所以我们可以编写另一个替代Foo实例:
instance (Bar a, S a ~ b) => Foo (X a b) where T (X a b) = a