作为介绍,请参阅此问题和我的答案。最后,我注意到您似乎可以消除对具有功能依赖性的无关类型规范的需要。现在的代码如下:
{-# LANGUAGE GADTs, MultiParamTypeClasses, FlexibleInstances, FunctionalDependencies #-}
data Nil
data TList a b where
TEmpty :: TList Nil Nil
(:.) :: c -> TList d e -> TList c (TList d e)
infixr 4 :.
class TApplies f h t r where
tApply :: f -> TList h t -> r
instance TApplies a Nil Nil a where
tApply a _ = a
instance TApplies f h t r => TApplies (a -> f) a (TList h t) r where
tApply f (e :. l) = tApply (f e) l]
现在,对我而言,直观的事情似乎是将Fundep添加f h t -> r
到 TApplies
类型类中。但是,当我这样做时,GHC抱怨TApplies
如下的递归实例:
Illegal instance declaration for
‘TApplies (a -> f) a (TList h t) r’
The coverage condition fails in class ‘TApplies’
for functional dependency: ‘f h t -> r’
Reason: lhs types ‘a -> f’, ‘a’, ‘TList h t’
do not jointly determine rhs type ‘r’
最后两行对我来说似乎是错误的,尽管我希望我只是误解了一些东西。我的推理如下:
a -> f
and a
,那么我们就拥有f
。TList h t
那么我们有h
和t
。TApplies f h t r
,我们就有了资金f h t -> r
。f
,h
而且t
,我们有r
。在我看来,这意味着功能依赖性已得到满足。有人可以指出我逻辑上的缺陷,也许还建议对功能依赖性进行更适当的选择?(请注意,f r -> h
GHC允许类似的操作,但似乎在类型指示方面没有太多余地。)
首先,我不知道为什么要使用如此复杂的数据类型-因此,我将使用简化的数据类型来解决您的问题。但是,相同的原理也适用于您的确切情况。
将数据类型定义为:
data TList (xs :: [*]) where
Nil :: TList '[]
(:.) :: x -> TList xs -> TList (x ': xs)
infixr 4 :.
然后您的班级少了一个类型参数:
class TApplies f (xs :: [*]) r | f xs -> r where ...
有问题的实例看起来像
instance TApplies g ys q => TApplies (y -> g) (y ': ys) q where
现在,检查产生的错误(与您的问题基本相同)-即,请注意,它说“覆盖条件在类'TApplies'中失败”。那么什么是“覆盖条件”?该用户指南有这样一段话:
覆盖条件。对于类的每个函数依赖项tvsleft-> tvsright,S(tvsright)中的每个类型变量都必须出现在S(tvsleft)中,其中S是将类声明中的每个类型变量映射到实例中对应类型的替换宣言。
这过于技术性,但是从根本上说,这是右侧的类型变量集-在这种情况下,即{q}
必须是左侧的变量集的子集-这里{y, g, ys}
。显然不是这样。
您会注意到覆盖条件没有说明实例的上下文。这是您问题的根源!在确定功能依赖项是否成立时,不考虑上下文。我想您会同意我的看法,即该实例显然失败了instance TApplies (y -> g) (y ': ys) q where ...
,这是编译器看到的。
解决方案很简单:添加{-# LANGUAGE UndecidableInstances #-}
到文件顶部。除其他外,覆盖条件的目的是确保实例解析终止。如果启用UndecidableInstances
,则表示“信任我,它将终止”。
附带说明一下,当前的公式使用起来不是很好。例如,tApply (+) (1 :. 2 :. Nil)
产生“歧义类型”错误或类似的错误。更糟糕的是,tApply (+) (1 :. "s" :. Nil)
会产生相同的确切“歧义类型”错误!对于任何尝试使用此类编写代码的人来说都没有太大帮助。您必须在各处指定单态类型签名,以使其按原样工作。
解决方案是使实例声明符合以下条件:
instance (a ~ a') => TApplies a '[] a' where
instance (TApplies g ys q, y ~ y') => TApplies (y -> g) (y' ': ys) q where
然后,第一种情况按原样编译,键入defaulting插入,并显示“ 3”。在第二种情况下,您得到No instance for (Num [Char])
,这实际上是有帮助的。
再次起作用的原因是,实例解析仅关心实例头,而不关心上下文。instance TApplies (y -> g) (y' ': ys) q
是编译器所看到的,显然y
并y'
可以是任意的,无关的,种类。只是到最后,当需要打印值时,编译器才需要解析y ~ y'
约束,此时您只需拥有表达式(+) 1 2
。
您可以按以下方式实现没有数据种类的类型:
{-# LANGUAGE GADTs #-}
data Cons a b
data Nil
data TList xs where
Nil :: TList Nil
(:.) :: x -> TList xs -> TList (Cons x xs)
但是,没有真正的理由这样做,因为DataKinds不会破坏其他有效的代码。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句