abstract type
在访问(在 julia v0.6 中)的字段时,我有一个关于类型不稳定性的问题
。
假设我有一个类型层次结构,所有这些都共享相同的实例变量。我知道正确访问该字段是类型不稳定且不能保证正确,因为有人总是可以定义一个缺少预期变量的新子类型。但是,即使将成员访问包装在函数中,访问仍然是类型不稳定的,我不知道为什么。
假设我们有一个简单的类型层次结构:
julia> begin
abstract type AT end
mutable struct T1 <: AT
x::Int
end
mutable struct T2 <: AT
x::Int
end
end
我们不是直接访问a.x
,而是将其包装在一个函数屏障中:
julia> getX(a::AT)::Int = a.x
>> getX (generic function with 1 method)
julia> @code_warntype getX(T1(1))
Variables:
#self# <optimized out>
a::T1
Body:
begin
return (Core.getfield)(a::T1, :x)::Int64
end::Int64
请注意,通过此方法进行的访问是类型稳定的,因为它可以推断出的类型a
是T1
。
但是,当我getX
在编译器无法提前知道变量类型的上下文中使用时,它仍然是类型不稳定的:
julia> foo() = getX(rand([T1(1),T2(2)]))
>> foo (generic function with 1 method)
julia> @code_warntype foo()
Variables:
#self# <optimized out>
T <optimized out>
Body:
begin
SSAValue(0) = (Core.tuple)($(Expr(:new, :(Main.T1), 1)), $(Expr(:new, :(Main.T2), 2)))::Tuple{T1,T2}
SSAValue(2) = $(Expr(:invoke, MethodInstance for rand(::Array{AT,1}), :(Main.rand), :($(Expr(:invoke, MethodInstance for copy!(::Array{AT,1}, ::Tuple{T1,T2}), :(Base.copy!), :($(Expr(:foreigncall, :(:jl_alloc_array_1d), Array{AT,1}, svec(Any, Int64), Array{AT,1}, 0, 2, 0))), SSAValue(0))))))
return (Core.typeassert)((Base.convert)(Main.Int, (Core.getfield)(SSAValue(2), :x)::Any)::Any, Main.Int)::Int64
end::Int64
请注意,它内联了的主体getX
,并将其替换为本质上
tmp.x::Int64
。这让我很吃惊,因为我期望getX
调度到我们上面看到的相同定义的两个实例之一,因为类型是已知的,所以不需要断言。
我认为如果实际上只为抽象基类型定义,这确实getX
是有道理的——不会有任何方法可以按照我想象的方式调度。所以我尝试重新定义,以便它为每个子类型生成一个特定的方法,如下所示:AT
getX
julia> getX(a::T where T<:AT)::Int = a.x
>> getX (generic function with 1 method)
但这实际上是一个相同的定义,并没有改变:
julia> methods(getX)
>> # 1 method for generic function "getX":
getX(a::AT) in Main at none:1
知道我怎样才能让它工作吗?