在haskell中编写只能处理相关类型的函数

时间:2011-10-09 00:17:18

标签: haskell typeclass associated-types

我正试图找到一种更优雅的方式来编写以下代码。

class C c where
    type E c :: * -> *

class C c => A c where
    g :: E c a -> E c a

class (C c, A c) => D c where
    f :: E c a -> E c a

instance A c => D c where
    f = g

这会产生错误。

Test.hs:58:9:
    Could not deduce (E c0 ~ E c)
    from the context (A c)
      bound by the instance declaration at Test.hs:57:10-19
    NB: `E' is a type function, and may not be injective
    Expected type: E c a
      Actual type: E c0 a
    Expected type: E c a -> E c a
      Actual type: E c0 a -> E c0 a
    In the expression: g
    In an equation for `f': f = g
Failed, modules loaded: none.

我目前的解决方案是添加一个虚拟变量,它可以从中派生出来 哪个特定的C正在使用中。

class C c where
    type E c :: * -> *

class C c => A c where
    g_inner :: c -> E c a -> E c a
g = g_inner undefined

class (C c, A c) => D c where
    f_inner :: c -> E c a -> E c a
f = f_inner undefined

instance A c => D c where
    f_inner = g_inner

我知道这是相关类型的另一个不是单射的实例, 但我无法弄明白。当然,E可能不是单射的,而是它 似乎某处 g 将对特定信息起作用的信息 D 中引用的(E c)已丢失。

任何解释,更重要的是更好的解决方法 非常感激。谢谢!

修改

好的,我看到将type切换为data会使代码生效。

我正试图说出这可能会起作用。每个c都会创建一个新的数据类型E c。在实例上下文中,我们必须将forall a. ((E) c) a -> ((E) c) aforall a. ((E) c) a -> ((E) c) a匹配。表示F = E c,然后我们将forall a. F a -> F a与其自身匹配。

我无法查看类型同义词(关联类型)的情况。当然,可以定义两个A的实例,它们都有签名(E c) a -> (E c) a。但是,为什么使用范围内的实例A c中的定义呢?

谢谢!

1 个答案:

答案 0 :(得分:8)

问题在于,仅从E c a -> E c a开始,编译器不知道选择哪个实例C

关联类型系列只是类型同义词。所以上课

class C c => A c where
    g :: E c a -> E c a
从typechecker的角度来看,也可能是

class C c => A c where
    g :: m a -> m a

由于未提及类变量c,因此无法确定应使用哪个实例字典来选择函数。虽然这是因为类型族不是单射的,但我同意从错误信息中可以看出这是问题所在。

使用Daniel Wagner建议的数据系列可能是最优雅的解决方案。我有时能够反转我的类型系列,因此我不会根据E c得到c,而是根据c选择E c。在这种情况下,这将给出:

class E (e :: * -> *) where
    type C e :: *

class E e => A e where
    g :: e a -> e a

class (A e) => D e where
    f :: e a -> e a

instance A e => D e where
    f = g

根据您正在做的其他事情,这可能对您有用。

除了这个问题,拥有一个单独的实例没有多大意义。由于A可用作超类约束,因此您只需将f = g设置为默认方法即可。这样做会少得多麻烦;你可能实际上并不想要instance D e where ...