在此示例中,我可以得出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)
但是我已经读过使用UndecidableInstances
可能是个坏主意,而且我不确定什么时候可以,什么时候不能。
我尝试了这个,并且有效:
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
,{ {1}}和Show
实例以及NFData
,Eq1
和Show1
实例以及NFData1
的类。
我有3个问题:
答案 0 :(得分:2)
这个确切的示例出现在GHC手册的Inferred context for deriving clauses部分中。如此处所述,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)
中的出现频率更高,因此它违反了Instance termination rules中记录的规则(特别是第一个Paterson条件),以确保类型检查器不会循环。因为这些规则足够但不是必需的,因此即使它们违反了规则,仍有许多实例声明可以正常工作。启用UndecidableInstances
可以启用它们。
没有特别的理由不打开UndecidableInstances
。在最坏的情况下,您将创建一个导致类型检查器循环的实例,并且编译将失败,并显示错误消息,建议您使用-freduction-depth={n}
增加堆栈深度(如果您拥有真正的版本,这将无济于事。环)。请注意,这只是一个编译时问题。如果代码使用UndecidableInstances
进行编译,则不必担心将来会出现运行时危险。
请注意,我不需要打开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