我正在尝试找到一种更优雅的方式来编写以下代码。
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) a
. forall 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
范围内的实例中的定义是错误的呢?
谢谢!!