Hey Julia! Новый язык, который выглядит, как Python, а летает, как C.
Social Engineering![](/file/53d716362d9b2cd51c2c2.png)
- История
- Характеристики
- Синтаксис
- Скорость
- Параллельные вычисления
- Дополнительные преимущества
- Где запускать
- Насущный вопрос
- Python или Julia
Julia — молодой язык программирования, предназначенный преимущественно для научных вычислений. Его создатели хотели, чтобы он занял нишу, которую прежде занимали Matlab, его клоны и R. Создатели пытались решить так называемую проблему двух языков: совместить удобство R и Python и производительность C. Посмотрим, что у них получилось.
![](/file/1b10174b5315836100cc3.png)
История
Разрабатывают язык в Массачусетском технологическом институте с 2009 года, а в начале 2012 года была выпущена публичная версия. Бета имела много проблем и интересовала только любопытных энтузиастов и разработчиков-альтруистов, которые старались приспособить окружения под постоянно меняющиеся стандарты.
Вот что пишут создатели Julia.
Мы хотим язык с открытым исходным кодом, с либеральной лицензией. Мы хотим скорость C с динамизмом Ruby. Нам нужен гомоиконичный язык с настоящими макросами, как Lisp, но с очевидными, знакомыми математическими обозначениями, такими как в Matlab. Мы хотим что-то такое же удобное для общего программирования, как Python, такое же простое для статистики, как R, такое же естественное для обработки строк, как Perl, такое же мощное для линейной алгебры, как Matlab, и способное склеивать программы вместе как оболочку. Нечто простое в освоении, но при этом радующее самых серьезных хакеров. Мы хотим высокой интерактивности и эффективной компиляции. Мы ведь не слишком многого просим, верно?
В августе 2018 года вышла версия 1.0.0, что породило бурный интерес к языку. Джулию начали преподавать в университетах США, появились онлайновые курсы (на Cursera и Julia Academy), стартовали коммерческие и исследовательские проекты, разработчики начали проявлять интерес, а владение этим языком вошло в топ самых востребованных профессиональных навыков по версии Upwork. Стабилизация синтаксиса, в свою очередь, вызвала взрывной рост разработки вспомогательных пакетов.
![](/file/1b10174b5315836100cc3.png)
Характеристики
- Скорость: этот язык разрабатывался для высокой производительности. Программы, написанные на Julia, компилируются в эффективный нативный код для разных платформ через LLVM.
- Общность: используя парадигму множественной диспетчеризации, Julia облегчает выражение многих объектно ориентированных и функциональных шаблонов программирования. Стандартная библиотека обеспечивает асинхронный ввод-вывод, управление процессами, ведение журнала, профилирование, менеджер пакетов и многое другое.
- Динамичность: Julia поддерживает динамическую типизацию, ведет себя как язык сценариев и имеет хорошую поддержку для интерактивного использования.
- Технологичность: Julia превосходна в высокотребовательных вычислениях, имеет синтаксис, который отлично подходит для математики, поддерживает множество числовых типов данных и параллелизм «из коробки». Мультиметод этого языка идеально подходит для определения числовых и массивоподобных типов данных.
- Опциональность: она имеет богатый язык описательных типов данных, и объявления типов могут использоваться для уточнения и укрепления программ.
Адаптивность: встроенная система управления пакетами, которые хорошо работают вместе. Матрицы единичных величин или таблицы данных, названий, чисел и изображений — все можно обработать с одинаково хорошей производительностью.
![](/file/1b10174b5315836100cc3.png)
Синтаксис
Julia выглядит лучше, чем Matlab, но на достижение это не тянет. Среди основных языков программирования закостенелость и плохой дизайн Matlab уступают только PHP. В Octave и Scilab некоторые из этих проблем исправили, но там есть и свои. Matlab и R берут тем, что у них огромные, собранные за годы библиотеки наборов инструментов и функций, с которыми легко решать научные и вычислительные задачи.
В арсенале Julia этого пока нет: потребуются годы, чтобы создать собственную настолько же полную библиотеку пакетов.
Как и в перечисленных языках (Python, Matlab и прочие во главе с легендарным Fortran), в Julia есть срезы ( Arr[:,1] ) арифметические операторы распространяются на массивы, а индексация начинается с единицы.
Но вернемся к синтаксису. Для начала создадим пару массивов-векторов:
julia> A = [2, 3, 4] 3-element Array{Int64,1}: 2 3 4 julia> B = [6+2im, 0.8, -1] 3-element Array{Complex{Float64},1}: 6.0 + 2.0im 0.8 + 0.0im -1.0 + 0.0im
Обрати внимание, что можно использовать разные числовые типы: тип подгоняется под высший в иерархии. Теперь создадим массив-строку и выполним элементарные операции:
julia> C = [1 0 1] 1?3 Array{Int64,2}: 1 0 1 julia> A+B 3-element Array{Complex{Float64},1}: 8.0 + 2.0im 3.8 + 0.0im 3.0 + 0.0im julia> C * ans.^2 1-element Array{Complex{Float64},1}: 69.0 + 32.0im
Результат последних вычислений хранится в переменной ans
, в нашем случае — массив, который получился при сложении А и В. Мы его поэлементно возвели в квадрат и скалярно умножили на С.
Давай посмотрим, как можно задавать функции.
function cube(x) x^3 end cube(x) = x^3
Функции возвращают результат последних вычислений, то есть return,
прописывать не обязательно. Есть возможность использовать анонимные функции (похоже на лямбды из Python).
Например, посчитаем евклидову норму вектора:
sqrt( sum(x-> x^2, Arr) )
Можно записать то же самое цепочкой функций (ведь это унарные операторы):
Arr.^2 |> sum |> sqrt
Нужные функции, как правило, уже есть в стандартной библиотеке. Для следующих двух примеров есть аналог — hypot(Arr)
. Со строками работать так же просто, как с массивами. Выполним конкатенацию и запросто реализуем шифр Цезаря:
julia> "russian" * ' ' * "hacker" "russian hacker" julia> caesar(X, n) = prod( [x+n for x in X] ) caesar (generic function with 1 method) julia> caesar("hakep.ru", 5) "mfpju3wz"
Поскольку задать функцию и множество для создания массива можно несколькими способами, есть и разные стили написания. Тру стори: думаешь, что за год хорошо разобрался в языке, раз задаешь массивы с помощью включений ( [2*i for i = 1:10]
), а потом однажды в чужом коде видишь 2*[1:10;]
и ничего не понимаешь. Так что неплохо поинтересоваться и итераторами тоже.
![](/file/1b10174b5315836100cc3.png)
Скорость
Скорость подкупает. Но если ты возьмешь и напишешь скрипт, выводящий энный член множества Фибоначчи, окажется, что вычисления идут медленно, не быстрее, чем на Python. Скорость Julia достигается за счет множественной диспетчеризации с последующей JIT-компиляцией.
Преимущество модели метода Julia в том, что она хорошо сочетается с диспетчеризацией по нескольким типам. При создании функции вида f(a,b)
, в зависимости от операций, которые используются внутри, будут определены методы для различных случаев. Скажем, f(a::Int, b::Int),
— для каждого будет скомпилирован высокоэффективный код, во многом идентичный тому, что дает C или Fortran. И ты можешь посмотреть этот код с помощью простых команд:
f(a::Float, b::Int), f(a::String, b::Int),
function f(a,b) return 2a+b end @code_native f(2.0,3.0) pushq %rbp movq %rsp, %rbp Source line: 2 vaddsd %xmm0, %xmm0, %xmm0 vaddsd %xmm1, %xmm0, %xmm0 popq %rbp retq nop @code_native f(2,3) pushq %rbp movq %rsp, %rbp Source line: 2 leaq (%rdx,%rcx,2), %rax popq %rbp retq nopw (%rax,%rax)
Эта избыточность замедляет загрузку интерпретатора, пакетов и первый запуск твоего скрипта. А так как интерпретатор имеет глобальную область видимости и не допускает специфику типов, то будет генерироваться громоздкий низкоуровневый код, который старается предусмотреть любую нестабильность типов.
Это решается легко: оборачивай свои операции в функции, избегай глобальных переменных и по возможности выстраивай логику программы, ориентируясь на конкретную задачу. Типы можно указывать и явно — как в статически типизированных языках. Кстати, мой пример шифратора Цезаря может принимать и строки, и массивы целых и комплексных чисел. Как думаешь, какой длины будет портянка LLVM, сгенерированная для этой функции?
![](/file/1b10174b5315836100cc3.png)
Параллельные вычисления
Как и остальные молодые языки вроде Go, Julia просто обязана быть ориентированной на многопроцессорные операции «из коробки». В наше время большие объемы вычислений выполняются в облачных средах, где по требованию выделяются нужные ресурсы. Стандарт MPI, который используют для организации работы крупномасштабных параллельных приложений, не отличается особой эластичностью. В ходе вычислений нельзя добавить процессоры, и возможности восстановления после сбоев весьма ограниченны.
В Julia передача сообщений отличается от MPI. Вместо модели «запрос — ответ» используется глобально распределенное адресное пространство, что позволяет легко оперировать ссылками на объекты на разных машинах и на ходу добавлять дополнительные ресурсы.
А еще это чертовски просто реализовать! Вызываем пакеты, которые позволят осуществлять распределенные вычисления и использовать общие для всех процессов массивы. Указываем, что нужно посчитать параллельно.
using Distributed, SharedArrays addprocs(2) a = SharedArray{Float64}(10) @distributed for i = 1:10 a[i] = i end
Или вычислим число пи одним из самых непрактичных способов на двух ядрах:
using Distributed, Statistics addprocs(2) @everywhere function PI(n) p = 0 for i = 1:n x = rand(); y = rand() p += (x^2 + y^2) <= 1 end 4p/n end find_PI(N) = mean( pmap( PI, [N/nworkers() for i = 1:nworkers()] ) ) julia> @time fPI(1_000_000_000) 2.674318 seconds (181 allocations: 7.453 KiB) 3.1416085999999996
В Julia используются такие техники, как Atomic Operations, Channels и Coroutines, а также MPI и Cuba.
Недавно была анонсирована новая способность Julia — Composable multi-threaded parallelism.
Так что, если тебе нужно обработать 150 Тбайт астрономических данных, повысить эффективность своей криптофермы, произвести климатическое моделирование, ты знаешь, какой инструмент выбрать.
![](/file/1b10174b5315836100cc3.png)
Дополнительные преимущества
Документация одна из самых удобных и полных среди тех, что я встречал. К тому же ты всегда можешь вызвать справку по любой функции прямо в консоли набора кода, например: ?prod, ?sqrt, ?*, ??.
Набор и использование в коде символов Юникода.
\lambda + <Tab> превратится в λ — приятная мелочь, хоть и не все консоли и браузеры ее отобразят.
Встроенный пакетный менеджер — парой команд можно скачать и настроить пакет, если он существует. Несмотря на увеличение числа пакетов, часто узкоцелевые пакеты заброшены или забагованы. В крайнем случае ты всегда можешь написать свой.
Макросы и другие возможности метапрограммирования. Это ускоряет оборачивание библиотек на других языках. Хороший пример метапрограммирования — в пакете StructArrays.jl, где очень элегантно решена проблема SoA/AoS.
Функции языка С можно вызывать напрямую, функции Python — при помощи PyCall. С другими популярными языками дружба тоже довольно тесная.
![](/file/1b10174b5315836100cc3.png)
Где запускать
REPL — консоль-интерпретатор. Качаем с официального сайта и получаем все возможности Julia из коробки.
Jupyter — удобный инструмент популярный у дата-сайентистов.
Juno — IDE с обозревателем переменных и всеми недостатками Atom.
Дебагер — то, чего так долго не хватало джулиистам-первопроходцам.
Cassette — возможность модифицировать компилятор.
![](/file/1b10174b5315836100cc3.png)
Плагины и редакторы:
![](/file/1b10174b5315836100cc3.png)
Насущный вопрос
Может быть, ты уже задался вопросом, почему Julia не поддерживает объектно ориентированное программирование. Но это не так.
Julia — мультипарадигмальный язык, который поддерживает методы из процедурного, декларативного, функционального, мета и — внимание! — объектно ориентированного программирования.
Здесь все является объектом, есть наследование, абстрактные классы, полиморфизм, инкапсуляция. Точнее, в Julia все есть множественная диспетчеризация (multiple dispatch). Вместо завязки на экземпляре объекта
instance.method будет метод, заточенный под объекты method(instance, …)
Недостаток в том, что типы и методы по умолчанию не связаны между собой: можно получить код, где для какого-то типа нет методов либо они есть, но конфликтуют. Практически, идиоматичная Julia просто объединяет все методы с одним и тем же именем.
julia> size size (generic function with 89 methods)
В результате у Julia по дизайну все полиморфно. Так что Julia объектно ориентирована до глубины души. Но это неочевидно из-за размытия границы между функциональным программированием и объектно ориентированным.
Создатели Julia были сосредоточены на разработке языка для поддержки математического программирования. Использование ООП в стиле С++ и Java, когда первый оператор владеет функцией, было неудобно в плане реализации и организации кода. Методы класса не очень-то полезны при работе с векторами, матрицами и тензорами, и наличие первого аргумента в выражении может привести к путанице.
Еще одна вещь, которую люди часто замечают, — поля данных не могут быть унаследованы, хотя не обязательно считать это неотъемлемой частью ООП, это просто свойство объектно ориентированных языков.
Множественная диспетчеризация — элегантное решение, которое дает программисту большую часть преимуществ ООП и в то же время не конфликтует с тем, как в нем работает математика.
![](/file/1b10174b5315836100cc3.png)
Python или Julia
Из-за пологой кривой обучения Julia почти идеальна в качестве первого языка программирования, с которого можно переучиться на Python или углубиться в C. Я слышал, как преподаватели сетуют на то, что Python калечит в студентах программистов и их потом трудно переучивать на что-то более близкое к железу.
Однако Julia не может делать все, что делает Python, хотя она применима для большинства практических целей. И, как любой язык программирования, она имеет свои подводные камни. Ты можешь использовать метапрограммирование и макросы, передавать их символьно в функции и из функций, но действительно передавать сами макросы нельзя. Функция eval всегда работает в области уровня модуля.
В Python метаклассы и проверки доступны везде, поскольку eval работает в локальной области.
Типы в Julia являются объектами, и ты можешь передавать конструкторы типов сколько угодно, но типы могут быть определены только на верхнем уровне модуля, и их нельзя изменить после объявления. Это важное отличие от динамических языков вроде Python, Ruby, JavaScript и Smalltalk. Хотя типы в Julia могут быть сгенерированы с помощью eval.
В Python новые классы разрешено создавать внутри функций, которые существуют только в этой локальной области, и атрибуты класса можно динамически исправлять во время выполнения.
Разработчики Julia добились впечатляющих успехов, но у их языка еще нет той опоры, которая обеспечивает долгосрочный успех. Даже сочетание удобства и скорости Julia не заставит поклонников Python ему изменить ( но это не точно ). Python уже имеет связь с относительно низкоуровневыми операциями в виде и даже упаковку Cython и даже упаковку для LLVM — Numba.
Для data science, machine learning, аналитики, научных вычислений и исследований Julia чертовски хороша. На ней удобно делать серверный бэкенд и прочие насущные сервисы, рассчитанные на долгую работу. Игры на ней писать можно, но с оглядкой на прожорливые вычисления и другие нагрузки. А вот кросс-платформенные приложения на Julia без плясок с бубном не получатся.
Я не знаю, выстрелит Julia или нет, но изучать и применять ее можно уже прямо сейчас.
![](/file/1b10174b5315836100cc3.png)
WWW
Для обучения на английском есть много ресурсов. На русском же из книг только устаревшая «Осваиваем язык Julia» Малкольма Шеррингтона. Еще есть статьи на Хабре и самоучитель на GitHub.
![](/file/1b10174b5315836100cc3.png)
Social Engineering - Канал посвященный психологии, социальной инженерии, профайлингу, НЛП, Хакингу, Анонимности и безопасности в сети интернет, Даркнету и все что с ним связано. Добро пожаловать ;-)
S.E.Book - Литература социального инженера.
@Social_Engineering_bot - Бот обратной связи.
![](/file/1b10174b5315836100cc3.png)