类型族和不连贯实例之间的奇怪交互
Strange interaction between type families and incoherent instances
此代码示例无法编译。
{-# LANGUAGE TypeFamilies, FlexibleInstances, UndecidableInstances, ScopedTypeVariables #-}
module IncoherentBug where
type family F a where
F () = Int
F a = a
class C a where
c :: a -> Int
instance C Int where
c y = y
instance {-# INCOHERENT #-} Monoid a => C a where
c _ = 0
class TwoPossible a where
x :: a
instance a ~ () => TwoPossible [a] where
x = []
instance TwoPossible Bool where
x = False
f :: (F a -> Int) -> [a] -> ()
f _ _ = ()
test = f (\v -> c v) x
基本上这里发生的是 f
的签名请求 x
的类型被解析为 [()]
,然后 v
的类型是 F ()
即 Int
,最后应选择 C
的第一个实例。相反,我得到了一个丢失的 Monoid Int
实例错误。
当我将 INCOHERENT
实例更改为 OVERLAPPABLE
时,代码编译正常。如果我用 Int
或 F ()
注释 v
,它也有效。如果我用 [()]
.
注释 x
(作为 f 的参数),它也会起作用
这是一个错误还是我误解了什么? ghc-mod 为 v
报告类型 F ()
即使我没有这样注释它。除了错误消息提到 Int
之外,这意味着类型检查器为 v
找出了正确的类型,但由于某种原因没有选择更具体的实例。
我可能还应该注意,我使用的是 GHC 8。我不知道这个问题是否出现在早期版本中。
GHC拒绝这段代码是完全正确的。您有一个 C (F a)
约束,它来自
f c :: C (F a) => [a] -> ()
当您打开 INCOHERENT
时,GHC 会立即将其减少为
f c :: Monoid (F a) => [a] -> ()
甚至不考虑参数的类型。这就是不连贯的意思——一个实例化可以提供一个更具体的实例,但一个不连贯的实例无论如何都会匹配。当然,实例 ... => C a
匹配 每个 类型,因此如果您的 C
约束出现在任何地方,该实例将立即匹配。
使用OVERLAPPABLE
等,C (F a)
约束不能通过选择Monoid a => C a
实例来减少,因为C Int
实例也可以匹配(这是连贯性,与不连贯性相反)。
如果你想看自己,向GHC询问f c
与INCOHERENT
和OVERLAPPABLE
的推断类型。
此代码示例无法编译。
{-# LANGUAGE TypeFamilies, FlexibleInstances, UndecidableInstances, ScopedTypeVariables #-}
module IncoherentBug where
type family F a where
F () = Int
F a = a
class C a where
c :: a -> Int
instance C Int where
c y = y
instance {-# INCOHERENT #-} Monoid a => C a where
c _ = 0
class TwoPossible a where
x :: a
instance a ~ () => TwoPossible [a] where
x = []
instance TwoPossible Bool where
x = False
f :: (F a -> Int) -> [a] -> ()
f _ _ = ()
test = f (\v -> c v) x
基本上这里发生的是 f
的签名请求 x
的类型被解析为 [()]
,然后 v
的类型是 F ()
即 Int
,最后应选择 C
的第一个实例。相反,我得到了一个丢失的 Monoid Int
实例错误。
当我将 INCOHERENT
实例更改为 OVERLAPPABLE
时,代码编译正常。如果我用 Int
或 F ()
注释 v
,它也有效。如果我用 [()]
.
x
(作为 f 的参数),它也会起作用
这是一个错误还是我误解了什么? ghc-mod 为 v
报告类型 F ()
即使我没有这样注释它。除了错误消息提到 Int
之外,这意味着类型检查器为 v
找出了正确的类型,但由于某种原因没有选择更具体的实例。
我可能还应该注意,我使用的是 GHC 8。我不知道这个问题是否出现在早期版本中。
GHC拒绝这段代码是完全正确的。您有一个 C (F a)
约束,它来自
f c :: C (F a) => [a] -> ()
当您打开 INCOHERENT
时,GHC 会立即将其减少为
f c :: Monoid (F a) => [a] -> ()
甚至不考虑参数的类型。这就是不连贯的意思——一个实例化可以提供一个更具体的实例,但一个不连贯的实例无论如何都会匹配。当然,实例 ... => C a
匹配 每个 类型,因此如果您的 C
约束出现在任何地方,该实例将立即匹配。
使用OVERLAPPABLE
等,C (F a)
约束不能通过选择Monoid a => C a
实例来减少,因为C Int
实例也可以匹配(这是连贯性,与不连贯性相反)。
如果你想看自己,向GHC询问f c
与INCOHERENT
和OVERLAPPABLE
的推断类型。