0

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

它接受语言及其扩展的描述,并输出一个新的 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,尽管我认为它在这里没有帮助。完成此操作后,我被告知尝试 UndecidableInstances ......这就是线索停止的地方。我仍然收到类型错误,但我不确定该怎么做。

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. 当然,这里不是这样的……

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

4

1 回答 1

3

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

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

里面的东西{-# #-}是一个编译指示,它是一种专门针对这种情况调用语言扩展的方法。OVERLAPPABLE表示允许有一个更具体的实例 ( ToA B),并根据偏好选择它。

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

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

于 2020-04-27T02:12:41.833 回答