更高级别类型的实例化和包含在统一期间如何交互?
How does instantiation of higher-rank types and subsumption interact during unification?
如果量词出现在逆变位置,则函数类型更高阶:f :: (forall a. [a] -> b) -> Bool
关于这种类型的统一,类型变量 a
比 b
更严格,因为以下实例化规则适用:
a
可以用灵活的类型变量实例化,前提是这不允许 a
脱离其范围
- 或使用另一个刚性类型变量
- 但不是非抽象类型,因为不是
foo
的调用者而是 foo
本身决定了 a
是什么,而 b
已经由来电者
然而,一旦包容开始发挥作用,事情就会变得更加复杂:
{-# LANGUAGE RankNTypes #-}
f :: (forall a. [a] -> [a]) -> Int -- rank-2
f _ = undefined
arg1a :: a -> a
arg1a x = x
arg1b :: [Int] -> [Int]
arg1b x = x
f arg1a -- type checks
f arg1b -- rejected
g :: ((forall a. [a] -> [a]) -> Int) -> Int -- rank-3
g _ = undefined
arg2a :: (a -> a) -> Int
arg2a _ = 1
arg2b :: (forall a. a -> a) -> Int
arg2b _ = 1
arg2c :: ([Int] -> [Int]) -> Int
arg2c _ = 1
g arg2a -- type checks
g arg2b -- rejected
g arg2c -- type checks
h :: (((forall a. [a] -> [a]) -> Int) -> Int) -> Int -- rank-4
h _ = undefined
arg3a :: ((a -> a) -> Int) -> Int
arg3a _ = 1
arg3b :: ((forall a. a -> a) -> Int) -> Int
arg3b _ = 1
arg3c :: (([Int] -> [Int]) -> Int) -> Int
arg3c _ = 1
h arg3a -- rejected
h arg3b -- type checks
h arg3c -- rejected
立即引起注意的是子类型关系,它会随着每个额外的逆变位置而翻转。申请 g arg2b
被拒绝,因为 (forall a. a -> a)
比 (forall a. [a] -> [a])
多态性更高,因此 (forall a. a -> a) -> Int
比 (forall a. [a] -> [a]) -> Int
多态性差。
首先我不明白的是为什么g arg2a
被接受了。仅当两个项都处于较高级别时才包含包含吗?
然而,g arg2c
类型检查的事实更让我困惑。这不是明显违反了刚性类型变量 a
不能用像 Int
这样的单型实例化的规则吗?
也许有人可以为这两个应用程序制定统一流程..
我们有
g :: ((forall a. [a] -> [a]) -> Int) -> Int
arg2c :: ([Int] -> [Int]) -> Int
应用于g arg2c
。
要进行类型检查,只要验证参数的类型是函数域类型的子类型就足够了。 IE。我们有
([Int] -> [Int]) -> Int <: ((forall a. [a] -> [a]) -> Int)
根据子类型规则,当且仅当 b<:b'
和 a'<:a
时,我们有 (a->b) <: (a'->b')
。所以上面相当于
Int <: Int
forall a. [a] -> [a] <: [Int] -> [Int]
第一个不等式是微不足道的。第二个成立是因为 foall
类型是每个实例的子类型。形式上,(forall a. T) <: T{U/a}
其中 {U/a}
表示用类型 U
替换类型变量 a
。因此,
forall a. [a] -> [a] <: ([a] -> [a]){Int/a} = [Int] -> [Int]
如果量词出现在逆变位置,则函数类型更高阶:f :: (forall a. [a] -> b) -> Bool
关于这种类型的统一,类型变量 a
比 b
更严格,因为以下实例化规则适用:
a
可以用灵活的类型变量实例化,前提是这不允许a
脱离其范围- 或使用另一个刚性类型变量
- 但不是非抽象类型,因为不是
foo
的调用者而是foo
本身决定了a
是什么,而b
已经由来电者
然而,一旦包容开始发挥作用,事情就会变得更加复杂:
{-# LANGUAGE RankNTypes #-}
f :: (forall a. [a] -> [a]) -> Int -- rank-2
f _ = undefined
arg1a :: a -> a
arg1a x = x
arg1b :: [Int] -> [Int]
arg1b x = x
f arg1a -- type checks
f arg1b -- rejected
g :: ((forall a. [a] -> [a]) -> Int) -> Int -- rank-3
g _ = undefined
arg2a :: (a -> a) -> Int
arg2a _ = 1
arg2b :: (forall a. a -> a) -> Int
arg2b _ = 1
arg2c :: ([Int] -> [Int]) -> Int
arg2c _ = 1
g arg2a -- type checks
g arg2b -- rejected
g arg2c -- type checks
h :: (((forall a. [a] -> [a]) -> Int) -> Int) -> Int -- rank-4
h _ = undefined
arg3a :: ((a -> a) -> Int) -> Int
arg3a _ = 1
arg3b :: ((forall a. a -> a) -> Int) -> Int
arg3b _ = 1
arg3c :: (([Int] -> [Int]) -> Int) -> Int
arg3c _ = 1
h arg3a -- rejected
h arg3b -- type checks
h arg3c -- rejected
立即引起注意的是子类型关系,它会随着每个额外的逆变位置而翻转。申请 g arg2b
被拒绝,因为 (forall a. a -> a)
比 (forall a. [a] -> [a])
多态性更高,因此 (forall a. a -> a) -> Int
比 (forall a. [a] -> [a]) -> Int
多态性差。
首先我不明白的是为什么g arg2a
被接受了。仅当两个项都处于较高级别时才包含包含吗?
然而,g arg2c
类型检查的事实更让我困惑。这不是明显违反了刚性类型变量 a
不能用像 Int
这样的单型实例化的规则吗?
也许有人可以为这两个应用程序制定统一流程..
我们有
g :: ((forall a. [a] -> [a]) -> Int) -> Int
arg2c :: ([Int] -> [Int]) -> Int
应用于g arg2c
。
要进行类型检查,只要验证参数的类型是函数域类型的子类型就足够了。 IE。我们有
([Int] -> [Int]) -> Int <: ((forall a. [a] -> [a]) -> Int)
根据子类型规则,当且仅当 b<:b'
和 a'<:a
时,我们有 (a->b) <: (a'->b')
。所以上面相当于
Int <: Int
forall a. [a] -> [a] <: [Int] -> [Int]
第一个不等式是微不足道的。第二个成立是因为 foall
类型是每个实例的子类型。形式上,(forall a. T) <: T{U/a}
其中 {U/a}
表示用类型 U
替换类型变量 a
。因此,
forall a. [a] -> [a] <: ([a] -> [a]){Int/a} = [Int] -> [Int]