Комментарии 10
Ужасен по сравнению с чем?
Для меня пока серьёзных неудобств только два: это отсутствие C-подобного for
и слишком много способов определить функцию:
# и так
function foo(x)
return x * 42
end
# и так
foo(x) = x * 42
# и так тоже
foo = x -> x * 42
Конкретно в этом примере код перегружен. Вопрос о причинах — к автору. Циклы в Julia используются редко. В большинстве случаев они заменяются применением функции к итератору. Например:
function mag(p::NDimPoint)
sqrmag = 0.0
# т.к. размерность точно неизвестна, нужно итерировать по всем полям структуры
# имена полей для типа T получаются вызовом fieldnames(T)
for name in fieldnames(typeof(p))
sqrmag += getfield(p, name)^2
end
return sqrt(sqrmag)
end
скорее встретится в форме:
mag(p::NDimPoint) =
sqrt(sum(name -> getfield(p, name)^2, fieldnames(typeof(p)))
Или
mag(p::NDimPoint) =
sum(name -> getfield(p, name)^2, fieldnames(typeof(p)) |>
sqrt
Если первое – то мультиметоды не так интересны, у нас есть :-)
Если второе – то интересно, как устроено и какие накладные расходы.
У Julia есть абстрактные и конкретные типы. Абстрактные всегда пустые, но могут наследоваться. Конкретные могут инстанцироваться и иметь поля. Для любого метода можно указать либо тип, к которому его конкретно надо примениять, либо указать, что тип абстрактный, и применять к любому из потомков.
То есть, ответ — полиморфизм статический. Однако методы для обработки наших типов можем писать где угодно. Обычно вместе с определением типа. Шаблоны C++ или Java generics — это, как раз, наиболее близкий аналог.
Комбинация того и того.
Если тип аргумента может быть выведен на этапе компиляции — диспетчеризуется статически. Если нет — то динамически. Динамически идёт через боксинг, выделение памяти и вообще дорого. Пример второго я привёл в статье, или можно посмотреть примеры в документации.
Хорошим тоном считается динамическую диспетчеризацию избегать, т.е. писать такой код, в котором тип возвращаемых из функций значений однозначно определяется типами аргументов.
Julia говорит "вы сначала определитесь, а потом просите".
Видео с JuliaCon про работу компилятора.
На стэковерфлоу рекомендуют ещё диссер Безансона почитать.
julia> a(x::Any, b::Float64) = nothing
a (generic function with 1 method)
julia> a(b::Float64, t::Any) = 1
a (generic function with 2 methods)
julia> methods(a)
# 2 methods for generic function "a":
[1] a(x, b::Float64) in Main at REPL[1]:1
[2] a(b::Float64, t) in Main at REPL[2]:1
julia> a(1.0, 1.0)
ERROR: MethodError: a(::Float64, ::Float64) is ambiguous. Candidates:
a(b::Float64, t) in Main at REPL[2]:1
a(x, b::Float64) in Main at REPL[1]:1
Possible fix, define
a(::Float64, ::Float64)
Stacktrace:
[1] top-level scope at none:0
Нет здесь динамики в понимании виртуальных функций ООП. Она выбирает метод чётко по типам. Если не может однозначно привести тип, то получите ошибку как в приведённом фрагменте выше. Впрочем, даёт рекомендацию о том, что делать:
Possible fix, define
a(::Float64, ::Float64)
Julia: типы, мультиметоды и арифметика над полиномами