Как стать автором
Обновить

Комментарии 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

mapreduce семантически действительно лучше, это я конкретно в этой публикации решил не вводить ещё и лямбды.


Хотя с тем, что циклы не нужны — не согласен. Если делать какой-то численный метод — внутри там придётся, скорее всего, таки придётся делать "Fortran in any language" (столкнулся с этим).

А полиморфизм (ну в смысле выбор реализации в зависимости от типа аргументов) статический (compile-time, как в шаблонах C++ или в Haskel) или динамический (как в виртуальных методах C++/C#/Java/...)?

Если первое – то мультиметоды не так интересны, у нас есть :-)
Если второе – то интересно, как устроено и какие накладные расходы.

У Julia есть абстрактные и конкретные типы. Абстрактные всегда пустые, но могут наследоваться. Конкретные могут инстанцироваться и иметь поля. Для любого метода можно указать либо тип, к которому его конкретно надо примениять, либо указать, что тип абстрактный, и применять к любому из потомков.


То есть, ответ — полиморфизм статический. Однако методы для обработки наших типов можем писать где угодно. Обычно вместе с определением типа. Шаблоны C++ или Java generics — это, как раз, наиболее близкий аналог.

Комбинация того и того.


Если тип аргумента может быть выведен на этапе компиляции — диспетчеризуется статически. Если нет — то динамически. Динамически идёт через боксинг, выделение памяти и вообще дорого. Пример второго я привёл в статье, или можно посмотреть примеры в документации.


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

Мне почему интересно: динамическая диспетчеризация для мультиметодов при наличии наследования может оказаться достаточно неоднозначной (например, f(a,b), перекрыты f(a:any,b:B) и f(a:A,b:any) – какой из методов использовать для f(a:A,b:B) и как определить, что именно этот метод? VMT для всех возможных комбинаций типов аргументов жирновата будет) – интересны приёмы решения в разных языках.
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)
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации