работает над Союзом типа в Юлии - PullRequest
1 голос
/ 13 октября 2019

В Джулии функция 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

Это полезно и отвечает на вопросы для многих, но не для всех типов. Сбои делятся на две категории, которые я заметил до сих пор:

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

1 Ответ

0 голосов
/ 13 октября 2019

Один из возможных ответов, который я понял после публикации вопроса, - это явное тестирование для типа 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}: # String => (Immutable (), true)
# Symbol => (Immutable (), true)
# Int64 => (Immutable (), true)
# Dict {Int64, String} => (Mutable (), false)
# Array => (Mutable (), false)
# Array {Int64,1} => (Mutable (), false)
# Tuple => (UnknownMutability (), false) # Tuple {} => (Immutable (), true)
# Tuple {Int64, String, Symbol} => (Immutable (), true)
# Union {String, Symbol} => (Immutable (), true)
# Union {String, Array} => (UnknownMutability (),false) # Union {Dict, Array} => (Mutable (), false) #

Для меня все это выглядит правильно. Однако я все еще хотел бы знать, есть ли другие решения, которые явно совпадают с Union, и есть ли другие случаи, когда эти функции не могут правильно определить изменчивость!

...