julialang: может (должна) эта ошибка типа быть поймана во время компиляции? - PullRequest
2 голосов
/ 14 июня 2019
function somefun()
    x::Int = 1
    x = 0.5
end

компилируется без предупреждения. конечно, вызов его вызывает InexactError: Int64 (0.5). вопрос: можете ли вы применить проверку времени компиляции?

Ответы [ 2 ]

2 голосов
/ 14 июня 2019

Юлия в этом смысле динамичный язык. Таким образом, нет, похоже, вы не можете определить, приведет ли результат присваивания к такой ошибке без предварительного запуска функции, так как проверка такого типа выполняется во время выполнения.

Я сам не был уверен, поэтому обернул эту функцию в модуль для принудительной (предварительной) компиляции при отсутствии запуска функции, и в результате не было выдано никакой ошибки, что подтверждает эту идею. (см. здесь , если вы хотите понять, что я имею в виду под этим).

Сказав это, ответьте на дух вашего вопроса: есть ли способ избежать неожиданного появления таких неясных ошибок времени выполнения?

Да, есть. Рассмотрим следующие две, почти эквивалентные функции:

function fun1(x     ); y::Int = x; return y; end;
function fun2(x::Int); y::Int = x; return y; end;

fun1(0.5)   # ERROR: InexactError: Int64(0.5)
fun2(0.5)   # ERROR: MethodError: no method matching fun2(::Float64)

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

Это тривиальный пример программирования "по контракту" с использованием элегантной системы проверки типов Джулии. Подробнее см. Проектирование по контракту .

Таким образом, ответ на ваш вопрос: да, если вы переосмыслите свой дизайн и будете следовать хорошим практикам программирования, чтобы такие ошибки обнаруживались на ранних этапах, то вы можете избежать их возникновения позже в непонятных сценариях, где они есть. трудно исправить или обнаружить.

Руководство Julia содержит руководство по стилю , которое также может помочь (пример, который я привел выше, находится прямо вверху!).

1 голос
/ 14 июня 2019

Стоит задуматься над тем, что на самом деле означает «время компиляции» у Джулии - потому что, вероятно, это не то, о чем вы думаете.

Когда вы определяете функцию:

julia> function somefun()
           x::Int = 1
           x = 0.5
       end
somefun (generic function with 1 method)

Вы не компиляция.На самом деле, Джулия не скомпилирует это, пока вы не позвоните.Компилятор Джулии можно рассматривать как Just-Barely-Ahead-Of-Time, в отличие от типичных конструкций JIT или AOT.

Теперь, когда вы вызываете функцию, она компилирует ее, а затем запускает ее, которая выдаетошибка.Вы можете видеть, что эта компиляция происходит в первый раз, когда вы вызываете функцию - она ​​занимает немного больше времени и памяти, поскольку она генерирует и кэширует специализированный код:

julia> @time try somefun() catch end
  0.005828 seconds (6.76 k allocations: 400.791 KiB)

julia> @time try somefun() catch end
  0.000107 seconds (6 allocations: 208 bytes)

Так что, возможно, вы можете увидеть это с помощью компиляции ДжулииДля модели не так важно, будет ли он пойман во время компиляции или нет - даже если Джулия отказалась скомпилировать (и кешировать) код, он будет вести себя точно так же, как вы сейчас видите.Он по-прежнему позволит вам определить функцию в первую очередь, и он все равно выдаст свою ошибку только при вызове функции.

Вопрос, который вы хотите задать, - это если Джулияможет (или должен) отловить эту ошибку во время функции определение .И тогда возникает вопрос: можно ли определить метод, который всегда приводит к ошибке?Как насчет функции, подобной error самой ?В Джулии совершенно нормально определить метод, который безусловно ошибается, как этот, и для этого могут быть веские причины.

Теперь есть способы спросить Джулию, если это так.в состоянии обнаружить, что этот метод всегда будет безоговорочно ошибаться:

julia> @code_typed somefun()
CodeInfo(
1 ─     invoke Base.convert(Main.Int::Type{Int64}, 0.5::Float64)::Union{}
└──     $(Expr(:unreachable))::Union{}
) => Union{}

Это самый первый шаг в процессе компиляции Джулии, и в этом случае он может видеть, что все, что за convert(Int, 0.5) равно unreachable -то есть это ошибки.Кроме того, он знает, что, поскольку функция никогда не вернется, ее тип возвращаемого значения равен Union{} (то есть, возможный тип не может быть возвращен!). Таким образом, вы можете попросить Джулию сделать этот шаг, например, с @inferredмакрос как часть набора тестов.

...