Вопрос о синтаксисе типа Julia: почему Array {Int32, 1} <: Array {Integer, 1} false? - PullRequest
3 голосов
/ 25 февраля 2020

В Джулии

Array{Int32, 1} <: Array{Integer, 1} 

оценивается как false, но

Array{Int32, 1} <: (Array{T, 1} where T <: Integer)

оценивается в true, поскольку Int32 <: Integer является истинным.

На мой взгляд, первое и второе выражения передают одну и ту же идею и должны оцениваться одинаково. Кроме того, первое выражение менее загромождено. Есть ли причина, по которой синтаксис Юлии оценивает первое как ложное, а второе как истинное? Есть ли что-то глубокое и хорошее в этом поведении или это упущение в том, как были разработаны массивы / система типов?

Ответы [ 3 ]

4 голосов
/ 25 февраля 2020

См. Параметр c Составные типы из руководства.

Бетон Point типы с различными значениями T никогда не являются подтипами друг друга:

julia> Point{Float64} <: Point{Int64}
false

julia> Point{Float64} <: Point{Real}
false

Предупреждение Этот последний пункт очень важен: хотя Float64 <: Real мы НЕ имеют Point{Float64} <: Point{Real}.

Другими словами, на языке теории типов параметры типа Юлии инвариантны , а не ковариантны (или даже контравариантны) .

Это по практическим причинам: хотя любой экземпляр Point{Float64} концептуально может быть похож на экземпляр Point{Real}, оба типа имеют разные представления в памяти:

  • Экземпляр Point{Float64} может быть компактно и эффективно представлен в виде непосредственной пары 64-битных значений;

  • Экземпляр Point{Real} должен содержать любую пару экземпляров Real. Поскольку объекты, которые являются экземплярами Real, могут иметь произвольный размер и структуру, на практике экземпляр Point{Real} должен быть представлен в виде пары указателей на индивидуально назначенные Real объекты.

2 голосов
/ 25 февраля 2020

Ключевое различие между Array{Integer, 1} и Array{T, 1} where T <: Integer заключается в том, что первый тип является конкретным типом, а последний - абстрактным типом. Причина этого заключается в том, что вы можете создать переменную типа Array{Integer, 1}. Это потенциально гетерогенный массив, поэтому он должен быть реализован в виде массива указателей (таких медленных и выделенных в куче). Имея это в виду, понятно, почему Array{Int32, 1} <: Array{Integer, 1}=false. Если мы напишем метод для специфицированного c типа Array{Integer, 1}, он не может быть специализированным, поскольку это уже конкретный тип, и мы будем работать с ошибкой при запуске его на Array{Int32, 1}, который имеет совершенно другой формат данных (встроенные элементы).

0 голосов
/ 25 февраля 2020

Было обсуждение о том, чтобы сделать поведение, которое вы описали, по умолчанию, но они, наконец, пришли к общему мнению, что ток может быть лучше.

По сути, это выбор синтаксиса того, что Vector{Integer} средства. Существует три типа отклонений на выбор:

julia> Vector{Int32} <: (Vector{T} where T <: Integer)
true

julia> Vector{Any} <: (Vector{T} where T >: Integer)
true

julia> Vector{Integer} <: (Vector{T} where {T >: Integer, T <: Integer})
true

Три типа отклонения соответствуют трем различным операциям над переменной. Ковариация для чтения, контрастность для записи и инвариантность для обоих. Например, в Rust &'a T является ковариантным относительно T, потому что вы можете только читать из него, в то время как &'a mut T является инвариантным, поскольку его можно читать и записывать. Если бы когда-либо существовал тип «только для записи», контраверсия была бы вменяемым значением по умолчанию. Данные переменные в Julia доступны как для чтения, так и для записи, поэтому выбор по умолчанию для инвариантности выглядит хорошим выбором.

...