GHC 推断我的模糊类型使约束成功
GHC infers my ambiguous type to make constraint succeed
在我实现类型级编码树的早期阶段,我遇到了 GHC 在其涉及类型约束时面对不明确类型时在类型推断中的特殊行为。我写了两个如下所示的 AST 节点,它们都可以通过它们实现的 Typed
type class instance:
检查它们的类型
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleContexts #-}
class Typed t where
type T t
-- | A Literal node
newtype Lit a =
Lit a
instance Typed (Lit a) where
type T (Lit a) = a
-- | A Plus Node
data Plus a b =
Plus a b
instance T a ~ T b => Typed (Plus a b) where
type T (Plus a b) = T a
然后我编写了未进行类型检查的 badPlus
函数,该函数不对函数参数执行 Typed
实例检查:
badPlus :: a -> b -> Plus a b
badPlus = Plus
badExample = Lit (1 :: Float) `badPlus` Lit 1 `badPlus` Lit 1
>:i badExample
badExample :: Plus (Plus (Lit Float) (Lit Integer)) (Lit Integer)
正如所见,GHC 将未注释的 (Lit 1)
推断为 (Lit Integer
),这并不奇怪。
现在到我的 goodPlus
我在签名上添加 Typed
约束:
goodPlus :: Typed (Plus a b) => a -> b -> Plus a b
goodPlus = Plus
goodExample = Lit (1 :: Float) `goodPlus` Lit 1 `goodPlus` Lit 1
>:i goodExample
goodExample :: Plus (Plus (Lit Float) (Lit Float)) (Lit Float)
我仍然期待 GHC 将两个未注释的类型推断为 Integer
但是,抱怨 Couldn't match type 'Float' with 'Integer
然而,令我惊讶(和高兴)的是我看到它将它们标记为 Float
s 使约束成功。
我的问题是:当涉及约束时,GHC 是否会改变其类型推断规则?涉及各种类型签名构造的类型推断的定义程序和优先级是什么?
这是这里发生的事情。当 GHC 尝试对表达式进行类型检查时:
goodPlus (Lit (1 :: Float)) (Lit 1)
反对签名:
goodPlus :: Typed (Plus a b) => a -> b -> Plus a b
这导致类型等式/约束:
a ~ Lit Float
b ~ Lit n
Num n
Typed (Plus (Lit Float) (Lit n))
为了解决这个 Typed
约束,GHC 将其匹配到:
instance T a' ~ T b' => Typed (Plus a' b')
与:
a' ~ Lit Float
b' ~ Lit n
(回想一下,实例定义中的约束在匹配过程中不起任何作用,所以匹配到这个实例没有问题。)这导致了一个额外的约束:
T (Lit Float) ~ T (Lit n) -- (*)
然而,T
是一个关联类型族,Typed (Lit a'')
的实例专用于 Typed (Lit Float)
和 Typed (Lit n)
允许 GHC 解析这些类型函数:
T (Lit Float) ~ Float
T (Lit n) ~ n
但是,这与上面的 (*) 一起允许 GHC 得出结论 Float ~ n
。
所以,最后的输入是:
goodPlus (Lit (1 :: Float)) (Lit 1) :: Plus (Lit Float) (Lit Float)
而且没有歧义。
在我实现类型级编码树的早期阶段,我遇到了 GHC 在其涉及类型约束时面对不明确类型时在类型推断中的特殊行为。我写了两个如下所示的 AST 节点,它们都可以通过它们实现的 Typed
type class instance:
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleContexts #-}
class Typed t where
type T t
-- | A Literal node
newtype Lit a =
Lit a
instance Typed (Lit a) where
type T (Lit a) = a
-- | A Plus Node
data Plus a b =
Plus a b
instance T a ~ T b => Typed (Plus a b) where
type T (Plus a b) = T a
然后我编写了未进行类型检查的 badPlus
函数,该函数不对函数参数执行 Typed
实例检查:
badPlus :: a -> b -> Plus a b
badPlus = Plus
badExample = Lit (1 :: Float) `badPlus` Lit 1 `badPlus` Lit 1
>:i badExample
badExample :: Plus (Plus (Lit Float) (Lit Integer)) (Lit Integer)
正如所见,GHC 将未注释的 (Lit 1)
推断为 (Lit Integer
),这并不奇怪。
现在到我的 goodPlus
我在签名上添加 Typed
约束:
goodPlus :: Typed (Plus a b) => a -> b -> Plus a b
goodPlus = Plus
goodExample = Lit (1 :: Float) `goodPlus` Lit 1 `goodPlus` Lit 1
>:i goodExample
goodExample :: Plus (Plus (Lit Float) (Lit Float)) (Lit Float)
我仍然期待 GHC 将两个未注释的类型推断为 Integer
但是,抱怨 Couldn't match type 'Float' with 'Integer
然而,令我惊讶(和高兴)的是我看到它将它们标记为 Float
s 使约束成功。
我的问题是:当涉及约束时,GHC 是否会改变其类型推断规则?涉及各种类型签名构造的类型推断的定义程序和优先级是什么?
这是这里发生的事情。当 GHC 尝试对表达式进行类型检查时:
goodPlus (Lit (1 :: Float)) (Lit 1)
反对签名:
goodPlus :: Typed (Plus a b) => a -> b -> Plus a b
这导致类型等式/约束:
a ~ Lit Float
b ~ Lit n
Num n
Typed (Plus (Lit Float) (Lit n))
为了解决这个 Typed
约束,GHC 将其匹配到:
instance T a' ~ T b' => Typed (Plus a' b')
与:
a' ~ Lit Float
b' ~ Lit n
(回想一下,实例定义中的约束在匹配过程中不起任何作用,所以匹配到这个实例没有问题。)这导致了一个额外的约束:
T (Lit Float) ~ T (Lit n) -- (*)
然而,T
是一个关联类型族,Typed (Lit a'')
的实例专用于 Typed (Lit Float)
和 Typed (Lit n)
允许 GHC 解析这些类型函数:
T (Lit Float) ~ Float
T (Lit n) ~ n
但是,这与上面的 (*) 一起允许 GHC 得出结论 Float ~ n
。
所以,最后的输入是:
goodPlus (Lit (1 :: Float)) (Lit 1) :: Plus (Lit Float) (Lit Float)
而且没有歧义。