你怎么能声称:(a)«抽象类型不能实例化»和(b)«T是抽象类型Type{T}»的实例?



我正在阅读有关Julia语言的类型(通常用于计算科学): https://docs.julialang.org/en/v1/manual/types/

在"抽象类型"标题下,我读到"抽象类型不能实例化"。作者解释说,抽象类型只是用来定义什么是其他事物的超类型。"好吧,这很有用,一切都很好"。但是,在标题"Type{T}类型选择器"下,我读到«Type{T}是一个抽象的参数类型,其唯一的实例是对象T»。这怎么可能?

因此,作者首先说,"抽象类型的一个定义特征是它不能被实例化",这是真的。然后作者说同样的陈述是错误的(因为T是抽象类型Type{T}的实例)。作者这些看似矛盾的说法是什么意思?

我在这里写的不是 Julia 核心开发人员的官方解释。

首先让我们了解Type{T}是什么,因为它是非标准的(这就是为什么在 Julia 手册中有一个单独的部分来介绍它)。我使用Int作为T具体来说。

第一:

julia> isabstracttype(Type{Int})
true
julia> isconcretetype(Type{Int})
false

这意味着Type{Int}被朱莉娅认为是抽象的而不是具体的。

在某些依赖于这两个函数的代码中,此注意事项是相关的(很少见,但可能)。

现在Type{Int}有什么特别之处?请看以下内容:

julia> typeof(Int)
DataType
julia> Type{Int} <: DataType
true
julia> DataType <: Type{Int}
false
julia> supertype(Type{Int})
Any
julia> isabstracttype(DataType)
false
julia> isconcretetype(DataType)
true

我们在这里看到了什么:

  • 如果检查Int类型,则为DataTypeDataType是具体的,而不是抽象的(与Type{Int}相反)。
  • 虽然DataType是具体的而不是抽象的,但它有一个子类型,例如Type{Int}.
  • 尽管Type{Int}DataType的子类型,但如果要检查它,它的超类型是Any

这不是故事的结局。现在检查没有参数的Type

julia> supertype(DataType)
Type{T}
julia> subtypes(Type{T} where T)
4-element Vector{Any}:
Core.TypeofBottom
DataType
Union
UnionAll
julia> supertype(DataType)
Type{T}
julia> subtypes(Type{T} where T)
4-element Vector{Any}:
Core.TypeofBottom
DataType
Union
UnionAll
julia> Type{Union{Int, Missing}} <: DataType
false
julia> Type{Vector} <: DataType
false
julia> Type{Vector{Int}} <: DataType
true

我们在这里了解到的是TypeDataType的超型。此外,并非所有的"类型说明符"都是DataType的子类型,因为UnionUnionAll类型不是DataType。这些注意事项在更高级的通用 Julia 代码中非常重要。

现在让我们讨论一下 Julia 手册使用的一个约定(即我讨论语句"要实例化"和"是一个实例"之间的区别):

  • 当它说某种类型可以实例化时,这意味着给定类型有一些值; 您可以使用typeof检查此具体类型;
  • 当它说某个值是某种类型的实例时,它遵循规则(我逐字复制它,因为所有写的东西都是相关的 - 本质上一个值可以是抽象类型的实例,也可以是具体类型的实例):

当附加到计算值的表达式时,:: 运算符被读作"is a instance"。可以在任何地方使用它来断言左侧表达式的值是右侧类型的实例。当右侧的类型是具体的时,左侧的值必须具有该类型作为其实现 - 回想一下,所有具体类型都是最终的,因此没有实现是任何其他实现的子类型。当类型是抽象的时,值由作为抽象类型的子类型的具体类型实现就足够了。


总结。在 Julia 中,需要添加一种方便的方式来传递类型作为值。我举两个例子:

julia> parse(Int, "12")
12
julia> rand(Int)
-2055168985030383807

因此,决定的设计是:

  • 使每个类型都成为抽象Type类型的实例;
  • DataType捕获显式声明的类型,如Int,但它不捕获UnionUnionAll之类的东西;每个显式声明的类型都是DataType的实例
  • 然而,DataType在许多情况下(特别是对于调度)过于模糊,此外,正如我们上面所说,它没有涵盖所有可能的类型规范;因此需要引入Type{T},即向Type添加参数;它是特殊的,这就是为什么Julia手册称它为Type{T}类型选择器(因为它的用途是类型选择器)。

为了满足所有这些要求:

  • IntDataType的实例,所以DataType是具体的;
  • 同时我们希望Int成为Type{Int}的实例;因为Type{Int}不可能是DataType的超类型,所以它必须是它的子类型,尽管DataType是具体的;因此它必须是抽象的,正如Julia手册所说:"具体类型不能相互子类型"(即具体类型不能有具体的子类型;实际上后来它还说"所有具体类型都是最终的。并且可能只有抽象类型作为它们的超类型",这似乎表明它不能有子类型;但实际上Type{Int}等是一个例外——即DataType允许具有抽象子类型;但是 Julia 确保不可能创建Type{Int}的具体子类型,因此主语句成立 - 不可能创建DataType的具体子类型 )

我同意这有点"玩文字游戏",但在实践中,拥有Type{T}是有用的,并且由于我上面描述的规则,它不会引入不一致。


说了这么多,让我参考一下你的陈述:

«抽象类型无法实例化»

这是真的,例如Type{Int}不能实例化,因为它是抽象的。

«Type{T} 是一个抽象的参数类型,其唯一的实例是对象 T»

这也是正确的,因为值可以是抽象类型的实例,例如IntType{Int}的实例,就像Julia中的任何值都是Any的实例一样。

最新更新