GHC 由于 UndecidableSuperClasses 而卡住 - 预期的行为或错误?

GHC stuck due to UndecidableSuperClasses - expected behaviour or bug?

以下代码片段使 GHC(已使用 8.6.2 和 8.4.4 检查)在编译期间卡住:

{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE UndecidableSuperClasses #-}

import GHC.Exts (Constraint)

data T = T

type family F t (c :: * -> Constraint) :: Constraint
type instance F T c = c T

class F t C => C t where

t :: C T => t
t = undefined

我认为它卡住了,因为对于 t GHC 试图找到 C T,这导致 F T C 通过类型族 F 扩展回 C T,这就是它要找的东西(无限循环)。

我想从理论上讲 GHC 可以告诉它它已经从自身达到了对 C T 的追求,并且任何依赖于自身的东西都可以递归地正常工作,或者我误解了什么?

旁注:在我偶然发现这种行为的真实示例中,通过将 UndecidableSuperClasses 替换为 Data.Constraint.Dict,我能够在不卡住编译器的情况下实现我想要的。

UndecidableSuperClasses 不会 使实例解析变慢。编译器仍将尽可能扩展超类约束。我相信实例字典中指向超类字典的字段是 strict,并且 GHC 实际上在编译时将它们固定下来。这与 UndecidableInstances 形成对比,它允许 实例约束 被(稍微)延迟处理。

deriving instance Show (f (Fix f)) => Show (Fix f)

会很好用。当为 Show (Fix Maybe)) 解析一个实例时,GHC 会发现它需要 Show (Maybe (Fix Maybe))。然后它看到它需要 Show (Fix Maybe)(它目前正在解析)并接受它,感谢 UndecidableInstances.

UndecidableSuperClases 所做的只是禁用保证展开不会循环的检查。查看 Ed Kmett's talk 开头附近的位,他在其中描述了过程 "reaching a fixed point"。

考虑一个工作示例(摘自 Data.Constraint.Forall):

type family Skolem (p :: k -> Constraint) :: k
class p (Skolem p) => Forall (p :: k -> Constraint)

GHC 仅接受 UndecidableSuperclasses。为什么?因为它不知道该约束可能意味着什么。据它所知,p (Skolem p) 可能会减少到 Forall p。这实际上可能发生!

class Forall P => P x

-- This definition loops the type checker
foo :: P x => x
foo = undefined