以下代码片段使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
我认为它卡住了,因为GHC试图找到t
,这导致C T
通过类型族F T C
扩展回{{1} },这就是它要查找的内容(无限循环)。
我认为从理论上讲,GHC可以说它达到了自己对F
的追求,而依赖于它的任何事物都可以递归地工作,还是我误会了某些东西?
旁注:在我偶然发现此行为的真实示例中,我可以通过用Data.Constraint.Dict
代替C T
来实现所需的功能,而不会卡住编译器。
答案 0 :(得分:5)
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开头附近的内容,他描述了“达到固定点”的过程。
考虑一个有效的示例(从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