Haskell 具有嵌套参数的类型的实例
在这个例子中,我可以推导出Eq:Haskell 具有嵌套参数的类型的实例,haskell,Haskell,在这个例子中,我可以推导出Eq: data A f t = A (f t) deriving (Eq) data B f t = B (f (f t)) deriving (Eq) 但在这个例子中: data A f t = A (f t) deriving (Eq) data B f t = B (f (f t)) deriving (Eq) 我得到这个错误: > No instance for (Eq (f (f t))) > arising from the fir
data A f t = A (f t) deriving (Eq)
data B f t = B (f (f t)) deriving (Eq)
但在这个例子中:
data A f t = A (f t) deriving (Eq)
data B f t = B (f (f t)) deriving (Eq)
我得到这个错误:
> No instance for (Eq (f (f t)))
> arising from the first field of ‘B’ (type ‘f (f t)’)
> Possible fix:
> use a standalone 'deriving instance' declaration,
> so you can specify the instance context yourself
这项工作:
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE StandaloneDeriving #-}
...
data B f t = B (f (f t))
deriving instance (Eq (f (f t)), Eq (f t)) => Eq (B f t)
但是我已经读到使用不可判定实例可能是个坏主意,我不确定什么时候可以,什么时候不行
我试过这个,效果很好:
data B f t = B (f (f t))
instance (Eq1 f, Eq t) => (Eq (B f t)) where
(B x) == (B y) = eq1 x y
但是我还想让B
成为NFData
,Read
和Show
的实例,我不想编写Read
,Show
和NFData
实例以及Eq1
,Show1
和NFData1
实例和NFData1
的类
我有3个问题:
为什么第一个示例编译而第二个示例不编译
现在是使用不可判定实例的时候吗
有没有什么方法可以不写所有这些实例就做我想做的事情
这个确切的例子出现在GHC手册的章节中。如文件所述,GHC在推断用于派生实例的上下文时采取保守立场,要求推断上下文中的每个约束必须仅包含类型变量,且不重复。由于(f(f t))
具有重复类型变量f
,因此它被拒绝。请注意,以下内容将被接受:
data C f g t = C (f (g t)) deriving (Eq)
手册中给出的解决方案是使用独立的派生子句,正如您所做的那样。正如评论中指出的,以下内容就足够了:
deriving instance Eq (f (f t)) => Eq (B f t)
但是,由于类型变量f
出现在约束中的频率比出现在“实例头”Eq(B f t)
中的频率更高,因此它违反了(特别是第一个Paterson条件)中记录的规则,这保证了类型检查器不会循环。因为这些规则是足够的,但不是必需的,所以有许多实例声明即使违反了规则也可以正常工作。启用不可判定实例
允许它们
没有特别的理由不启用不可判定实例
。在最坏的情况下,您将创建一个导致类型检查器循环的实例,编译将失败,并出现一个错误,建议您使用-fredectiondepth={n}
(如果您有一个真正的循环,这将不会有帮助)。请注意,这只是编译时问题。如果代码是使用不可判定实例编译的
,您就不必担心将来会出现运行时危险
请注意,我不需要启用FlexibleContexts
。以下工作很好:
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE UndecidableInstances #-}
data B f t = B (f (f t))
deriving instance Eq (f (f t)) => Eq (B f t)
deriving instance Read (f (f t)) => Read (B f t)
deriving instance Show (f (f t)) => Show (B f t)
对于手动派生的实例,以下操作似乎有效,不需要NFData1
或类似操作:
instance NFData (f (f t)) => NFData (B f t) where
rnf (B fft) = rnf fft
请注意,实际上并不需要Eq(ft)
。只要导出实例Eq(f(f t))=>Eq(B f t)
就可以了。(它仍然需要不可判定的实例
)@JosephSible如果没有Eq(f(f t))
和Eq(f(f t)
,我无法编译它,你确定吗?看看我的答案——它对我有用。@JosephSible,@K.A.Buhr,哎呀,我错了。这是一个简化的示例,在原始版本中它是data B f t=B(f(f t))(f t)
。那一个在上下文中需要额外的(f t)
。