我编写了一些需要-XUndecidableInstances来编译的Haskell代码。我确实理解为什么会发生这种情况,因为某种情况会受到侵犯,因此GHC会大喊大叫。
但是,我从来没有遇到类型检查器实际上会挂起或在无限循环中结束的情况。
非终止实例定义是什么样的 - 您能举个例子吗?
答案 0 :(得分:39)
在this paper from HQ中有一个经典的,有点令人担忧的例子(涉及与功能依赖的交互):
class Mul a b c | a b -> c where
mul :: a -> b -> c
instance Mul a b c => Mul a [b] [c] where
mul a = map (mul a)
f b x y = if b then mul x [y] else y
我们需要mul x [y]
与y
具有相同的类型,因此,我们需要x :: x
和y :: y
instance Mul x [y] y
根据给定的实例,我们可以拥有。当然,对于某些y ~ [z]
,我们必须z
这样
instance Mul x y z
即
instance Mul x [z] z
我们正在循环中。
这令人不安,因为Mul
实例看起来像它的递归在结构上是减少,这与Petr的答案中明显的病态实例不同。然而,它使GHC循环(无聊阈值开始以避免挂起)。
尽管y ~ [z]
在功能上依赖于z
这一事实,但我确信我曾在某处提到过这个问题y
。如果我们对函数依赖使用函数表示法,我们可能会看到约束显示y ~ Mul x [y]
并拒绝替换,因为它违反了事件检查。
好奇,我以为我会给它一个旋转,
class Mul' a b where
type MulT a b
mul' :: a -> b -> MulT a b
instance Mul' a b => Mul' a [b] where
type MulT a [b] = [MulT a b]
mul' a = map (mul' a)
g b x y = if b then mul' x [y] else y
启用UndecidableInstances
后,循环超时需要一段时间。禁用UndecidableInstances
后,实例仍然被接受且类型检查器仍然循环,但截止发生的速度要快得多。
所以...有趣的旧世界。
答案 1 :(得分:20)
例如:
{-# LANGUAGE UndecidableInstances #-}
class C a where
f :: a -> a
instance C [[a]] => C [a] where
f = id
g x = x + f [x]
发生了什么:输入f [x]
时,编译器需要确保某些x :: C [a]
的{{1}}。实例声明表明a
只有x
类型才能为C [a]
类型。因此编译器需要确保C [[a]]
等等,并在无限循环中被捕获。
另见Can using UndecidableInstances pragma locally have global consequences on compilation termination?