当我们说T1比T2具有更多的多态性时,我们是什么意思?

Shou Ya

我正在使用论文针对任意等级类型的实用类型推断来学习类型推断,并且我从一开始就坚持使用。我基本上对多态而不是关系的概念感到困惑,因此无法继续。

在其第3.3节中指出:

如果参数的类型比函数的参数类型多态,则该参数对于函数是可接受的。

在我的理解中,说T1的多态性比T2说任何具有type的实例T2必须满足type的多T1因此,按照我的定义,forall a. a比更具多态性Intforall a b. a -> b -> bforall a. a -> a -> a具有更多的多态性

据我了解,存在冲突。鉴于:

f :: (forall a. a) -> Int
k :: (forall a. a -> a)

(f k)显然是有效的[1]。然后,根据文章中的引文,forall a. a -> a应将多态性比forall a. a但是,举个例子,文字1会满足,forall a. a而显然不是forall a. a -> a,因此根据我的定义,它forall a. a应该比更具多态性forall a. a -> a这与文章中有关多态而非关系的描述相矛盾

我正在寻找有关此关系确切含义的示例的清晰解释。谢谢。

更新

[1]:d8d0d65b3f7cf42在评论中注意到我与(forall a. a)不兼容(forall a. a -> a)我的理解应该有问题。我注意到(forall a. a -> Int),而可以接受类型的参数(forall a. a -> a),而(forall a. a) -> Int不能。我不知道为什么这种情况会这样。

不管我的理解有误,我仍然希望对什么是多态性而不是关系性有一个很好的解释谢谢 :)

安德拉斯·科瓦奇(AndrásKovács)

正如对OP的注释中所指出的,(undefined :: (forall a . a) -> Int) ( undefined :: (forall a. a -> a) )它不进行类型检查,并且实际上forall a. a -> a并没有比更具多态性forall a. a,因此这里没有矛盾。

另一方面,要进行类型检查,(undefined :: (forall a. a -> Int)) (undefined :: (forall a. a -> a))所以forall a. a -> a必须a在函数类型中包含对吧?看起来似乎违反直觉,但这确实是事实。让我解释。

包含和类型变量

包含(或替代:多态子类型化)的意思是“至少与多态相同”,因此它更像是<=而不是<在更正的OP示例中,a将其包含在内,forall a. a -> a因为在该上下文 a中,该变量是非刚性类型的变量,并且可以将其与其他类型统一因此,forall a. a -> a <= typeVar b被认为是正确的,同时也产生了约束typeVar b = forall a. a -> a(或者可能是不同的约束,取决于我们对类型推断系统的选择。实际步骤可能与我在下面的示例中概述的步骤明显不同)。

一个简单的分步示例:

-- goal 
(forall a. a -> Int) <= (Int -> Int)
-- instantiate variable on the left hand side with a fresh type variable.
(tvar a' -> Int) <= (Int -> Int)
-- check return type subsumption
Int <= Int -- OK
-- check argument type subsumption
Int <= tvar a' -- OK, add "tvar b = Int" to the set of constraints.
-- Done.

通常,如果我们有forall a. P <= Q一些PQ,我们必须找到满足包含关系的某些特定实例a这意味着我们a用一个灵活的类型变量实例化,然后从那里开始。在这种情况下,我们将搜索特定类型,然后继续进行类型变量的优化。

另一方面,如果我们有P <= forall a. Q,则对于所有可能的实例化,必须包含a在右侧。在这种情况下,我们通常将实例a化为刚性(或skolem)类型的变量。刚性类型变量并不是真正的“变量”。相反,它代表某种任意(未知)固定类型,我们无法对其进行优化。具有刚性变量的示例:

-- goal
(forall a. a -> Int) <= (forall b. b -> Int)
-- instantiate variable on the left
(tvar a' -> Int) <= (forall b. b -> Int)
-- instantiate variable on the right
(tvar a' -> Int) <= (skolem b' -> Int)
-- check return types
Int <= Int -- OK
-- check argument types
skolem b' <= tvar a' -- OK, record the "tvar a' = skolem b'" constraint 
-- Done. 

基本上,我们只能使用skolem变量做两件事:

  1. 我们可以自由地得出结论skolem a <= skolem a,即他们可以自我包容。
  2. 我们可以使用一个灵活的变量来统一它们,即我们可以拥有skolem a <= tvar btvar b <= skolem a

旋转函数类型

我们已经在(a -> b) <= (a' -> b')iffb <= b'a' <= a的示例中看到了<=翻转参数类型。这是为什么?

假设我们的键入上下文期望type的某些功能a -> b此函数(通常作为函数)会消耗type的a值并产生type的b从某种意义上说,上下文-s需求b,并且还可以为我们的函数提供 a-s。如果函数返回的类型比通用b,那就很好了,因为那样的话,它也可以满足上下文对的需求b但是,如果函数期望的类型比通用a,则上下文会被阻塞。语境不佳的b库存只有-s,它可以使它们专门化,但不能对其进行概括。如果仅函数对它接受的类型不太挑剔

按照行话来说,函数类型构造函数的实参类型是协变的(或为负),而返回类型则是协变的(或为正)。Wikipedia上有一个关于方差的很好的讨论它更关注与OOP类层次结构有关的子类型化,而不是多态子类型化,但是它也应该为后者提供有用的见解。

让我们看一个例子,其中包含翻转更显着:

-- goal 
((forall a. a -> a) -> Int) <= ((forall a. a -> Int) -> Int)
-- remember: the context can supply "forall a. a -> Int"-s and demands "Int"-s. 
-- check return types
Int <= Int -- OK
-- check argument types
(forall a. a -> Int) <= (forall a. a -> a)
-- instantiate on the left
(tvar a' -> Int) <= (forall a. a -> a)
-- instantiate on the right
(tvar a' -> Int) <= (skolem a'' -> skolem a'')
-- check return types
Int <= skolem a'' -- FAIL: this clearly does not hold.
-- Abort.

等级1与等级较高设置中的函数类型差异

我的前两个示例没有嵌套forall-s,即它们只有等级1类型。您可能已经注意到,翻转的推定没有任何区别!如果我们假设(a -> b) <= (a' -> b') iff b <= b' and a <= a'存在,那么这两个示例都将进行类型检查,因为我们只需要检查类型变量,skolem和单态类型的使用即可。

因此,协方差-协方差的区别仅与较高级别的类型相关(这在OP引用的论文的第3.3节中也有暗示)。尽管,如果我没记错的话,尽管HMF系统排名较高,但它也忽略了函数方差自旋。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

当我们说Hashtable或Vector已同步时,这是什么意思?

当我们说我们正在使用Linux时,我们到底是什么意思?

当我们说同步(实例字段)..是什么意思?

当我们说一个ArrayList不同步是什么意思?

当我们说uintptr存储指针值的未解释位是什么意思?

为什么我们使用具有多态性的指针?

当我们在R中使用Str()函数时,“对象”是什么意思

在Swift 3中,当我们创建dispatchWorkItem时,DispatchWorkItemFlags参数是什么意思?

当我们说它自己的库的私有实例变量是什么意思?

当我们说存储同步上下文时,所有存储的是什么?

当我们说在执行程序时将操作系统的控制权传递给main()函数时,这是什么意思?

当我们在美元符号和括号内放置命令时,它在shell中是什么意思:$(command)

“ ValueError:将符号张量馈送到模型时,我们期望这些张量具有静态批大小”是什么意思?

为什么我们实际上需要运行时多态性?

在类映射中,当我们有迭代器时,const_iterator的必要性是什么?

当我们在创建 Spark 会话时传递设置元组“spark.some.config.option”、“config-value”时,我们的意思是什么?

我们所说的“文档重排”是什么意思,并且我们有重排文档的标准规范吗?

当我们有多个目标时,LASSO在sklearn中的目标函数是什么?

当我们有函数和 lambdas 时,C++ 中的“块”是什么?

当我们拥有图形卡时,多核CPU的目的是什么?

当我未触及文件时,Git为什么说文件被“我们删除”?

为什么我们在gcc中使用-lpcap,这是什么意思?

#import是什么意思?如果我们使用#include会发生什么?

当我们说Java中Byte的宽度是8位时,这意味着什么?

SwifUI中的“身份”是什么意思,以及我们如何更改某些事物的“身份”

FabricNotReadableException是什么意思?我们应该如何应对呢?

当我们需要返回比以前更多的值时,保持函数的向后兼容性

Java中的多态性:为什么我们将父引用设置为子对象?

Typescript 中语句“export type T1 = object & ComponentOptions<T1, T2>”的“&”是什么意思?