В Джулии функция 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
Это полезно и отвечает на вопросы для многих, но не для всех типов. Сбои делятся на две категории, которые я заметил до сих пор:
- Типы, для которых этот шаблон работает, но которые дают неправильный ответ относительно документации Джулии. Единственные примеры, которые яОбнаружены String и Symbol , которые предположительно неизменны, но возвращают false. Об этом есть сообщения об ошибках, поэтому мое решение - просто написать методы для этих типов, явно переопределяя поведение
- Типы, для которых этот шаблон не срабатывает. К ним относятся
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
функция неправильно маркируется?