由于我不确定我是否理解这个问题,所以我将讨论您编写的代码;也许我漫无边际的某些部分会为您指明一个有用的方向,或者引发一些我可能回答的尖锐问题。也就是说:警告!前面漫无边际的不回答!
首先,让我们谈谈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