Haskell-重叠实例和转换类型类



我正在编写通过数学逻辑中的定义实现扩展的代码。

它接受了对语言及其扩展的描述,并输出了一个新的haskell文件,该文件将把一种高级语言解析为一种低级语言。当然,如果我能把C语言变成B语言,把B语言变成A语言,那么通过作曲,我就能把C变成A。。。

以下是我面临的问题的一个最简单的例子:

data A = EmptyA | NodeA A A
data B = EmptyB | NodeB B B | UnaryB B
data C = EmptyC | NodeC C C | UnaryC C | TernaryC C C C

class ToA a where
convertToA :: a -> A
class ToB a where
convertToB :: a -> B

instance ToA B where
convertToA EmptyB      = EmptyA
convertToA (NodeB l r) = NodeA (convertToA l) (convertToA r)
convertToA (UnaryB l)  = NodeA (convertToA l) EmptyA
instance ToB C where
convertToB EmptyC           = EmptyB
convertToB (NodeC l r)      = NodeB (convertToB l) (convertToB r)
convertToB (UnaryC l)       = UnaryB (convertToB l)
convertToB (TernaryC l m r) = NodeB (convertToB l) (NodeB (convertToB m) (convertToB r))

-- instance (ToB a) => ToA a where
--   convertToA = convertToA . convertToB
-- I shouldn't have to write this
instance ToA C where
convertToA  = convertToA . convertToB

直观地说,instance (ToB a) => ToA a没有错,但编译器不喜欢它。代码按原样正确编译,但在用注释版本替换显式ToA C实例时,我收到以下错误:


minimal.hs:25:21: error:
• Illegal instance declaration for ‘ToA a’
(All instance types must be of the form (T a1 ... an)
where a1 ... an are *distinct type variables*,
and each type variable appears at most once in the instance head.
Use FlexibleInstances if you want to disable this.)
• In the instance declaration for ‘ToA a’
|
25 | instance (ToB a) => ToA a where
|                     ^^^^^

当然,我并不害怕语言扩展,所以我会按照指示添加FlexibleInstances,尽管我认为这在这里没有帮助。完成此操作后,我被告知要尝试UndicatedInstances。。。这就是线索的终点。我仍然收到一个类型错误,但我不确定该怎么办。

minimal.hs:29:16: error:
• Overlapping instances for ToA B
arising from a use of ‘convertToA’
Matching instances:
instance ToB a => ToA a -- Defined at minimal.hs:28:10
instance ToA B -- Defined at minimal.hs:16:10
• In the first argument of ‘(.)’, namely ‘convertToA’
In the expression: convertToA . convertToB
In an equation for ‘convertToA’:
convertToA = convertToA . convertToB
|
29 |   convertToA = convertToA . convertToB
|                ^^^^^^^^^^

这个错误消息让我特别困惑,因为我只有一个ToA B的定义。如果B本身是ToB的实例,例如通过设置convertToB = id,则此错误将更有意义。当然,这里不是这样的。。。

我该如何正确处理这个问题?提前感谢!^_^

您正在做正确的事情。您对Overlapping instances警告的谨慎态度是正确的。在这种情况下,它是连贯的。而且你不怕语言扩展,所以你想要:

instance {-# OVERLAPPABLE #-} (ToB a) => ToA a where
convertToA = convertToA . convertToB

{-# #-}中的那个东西是一个pragma,它是一种非常定制的方式来调用语言扩展,仅用于此实例。OVERLAPPABLE意味着允许有一个更具体的实例(ToA B(,并根据偏好选择。

您的约束(ToB a) =>实际上是Undecidable,因为它不小于实例头。与重叠相比,UndecidableInstances是一个相对"安全"的扩展。

对于重叠,不安全的使用是INCOHERENT(就像听起来一样糟糕(或"孤立实例"——有时会给你一个编译器警告;此处不适用,因为所有实例都与类声明位于同一模块中。

最新更新