在Julia中,isimmutable
函数应该可以告诉您对象何时不可变。但是,它不适用于类型,因此我想为此编写一个版本。根据我在其他地方找到的建议,一个好的开始是:
isimmtype(t::Type) = error("Type $t has not defined isimmtype")
isimmtype(t::DataType) = begin
if !t.isconcretetype
error("Abstract types are neither immutable nor mutable")
else
return !t.mutable
end
end
这很有用,可以回答很多但不是全部类型的问题。到目前为止,失败分为两类:
Union
和UnionAll
类型。两者似乎都有简单的解决方案。在UnionAll
的情况下,我们可以使用类型变量的上限对类型的验证进行测试。对于Union{A,B}
,我们可以比较A
和B
类型的可变性,并推论并集的可变性。我继续将这些解决方案写成我认为是合理的类似特征的语法,并且部分起作用:
"""
Mutability
The Mutability type is an abstract trait type with children Mutable, Immutable,
and UnknownMutability.
"""
abstract type Mutability end
struct Mutable <: Mutability end
struct Immutable <: Mutability end
struct UnknownMutability <: Mutability end
const MUT_TYPE = Mutable()
const IMM_TYPE = Immutable()
const UNK_TYPE = UnknownMutability()
"""
mutability(obj)
Yields an object of type Mutable, Immutable, or UnknownMutability depending on
whether the given type object is mutable, immutable, or unknown.
"""
mutability(T::Type) = UNK_TYPE
isimmtype(T::Type) = IMM_TYPE === mutability(T)
mutability(T::DataType) = begin
if !T.isconcretetype
return UNK_TYPE
elseif T.mutable
return MUT_TYPE
else
return IMM_TYPE
end
end
mutability(::Core.TypeofBottom) = UNK_TYPE
mutability(T::UnionAll) = mutability(T{T.var.ub})
mutability(::Type{String}) = IMM_TYPE
mutability(::Type{Symbol}) = IMM_TYPE
# This one causes problems:
mutability(::Type{Union{A,B}}) where {A,B} = begin
let mA=mutability(A), mB=mutability(B)
if mA === UNK_TYPE || mB === UNK_TYPE || mA !== mB
return UNK_TYPE
else
return mA
end
end
end
如果定义了所有这些方法,但最后一个方法,则mutability
函数将按我的预期工作,但Union{A,B}
类型的一个例外是,它们始终标记为未知。但是,如果定义了最后一个方法,则它会与Int64
之类的类型匹配,而没有在函数体中绑定B
(即,在方法开头添加println(A, B)
会导致错误,原因是B
不会被定义)。我可以看到A <: Union{A,B}
中存在歧义,但是如何在查询中明确匹配Union
类型呢?在这种情况下,如何防止Type{Union{A,B}}
与Type{A}
匹配?
另外:mutability
函数是否还有其他标签错误的情况?
自发布问题以来,我意识到的一个可能的答案是在默认方法中显式测试Union
类型:
mutability(T::Type) = begin
if typeof(T) !== Union
return UNK_TYPE
else
let mA=mutability(T.a), mB=mutability(T.b)
if mA === UNK_TYPE || mB === UNK_TYPE || mA !== mB
return UNK_TYPE
else
return mA
end
end
end
end
使用上述其他方法,可以正确进行以下所有测试:
map(k->k=>(mutability(k), isimmtype(k)),
[String, Symbol, Int64,
Dict{Int64,String}, Array, Array{Int64,1},
Tuple, Tuple{}, Tuple{Int64,String,Symbol},
Union{String,Symbol}, Union{String,Array}, Union{Array,Dict}])
#12元素数组{Pair,1}:#字符串=>(Immutable(),true)#符号=>(Immutable(),true)#Int64 =>(Immutable(),true)#Dict {Int64,String} =>(Mutable(),假)#Array =>(Mutable(),false)#Array {Int64,1} =>(Mutable(),假)#元组=>(UnknownMutability(),假)#Tuple {} =>(Immutable(),true)#Tuple {Int64,String,Symbol} =>(Immutable(),true)#Union {String,Symbol} =>(Immutable(),true)#Union {String,Array} =>(UnknownMutability(),false)#Union {Dict,Array} =>(Mutable(),假)#
所有这些对我来说都是正确的。但是,我仍然想知道在Union
上是否有其他明确匹配的解决方案,以及是否存在其他这些函数无法正确检测可变性的实例!