![Ранжирование языков программирования по их выразительности Ранжирование языков программирования по их выразительности](/storage/images/38/80/34/32/derived/faced062524c2f37c52bd39bdc59b199.jpg)
Можно ли составить рейтинг языков программирования в зависимости от их эффективности или выразительности? Другими словами, можно ли сравнить, насколько просто вам удается описать на них ту или иную концепцию? Косвенным показателем этого является количество строк кода, которые приходится изменить при каждом коммите. Этот параметр позволяет судить, насколько выразительно вы можете излагать содержание на том или ином языке в одинаковом объеме кода. Поскольку количество багов в коде прямо пропорционально количеству строк в исходнике, а не количеству идей, которые удалось выразить, более выразительный язык заслуживает более детального изучения хотя бы по этой причине (см., например, метрики сложности Холстеда).
Недавно в моем распоряжении оказалась великолепная подборка данных с сайта Ohloh. На нем отслеживается активность работы с репозиториями открытого кода. Так вот, я получил информацию о том, какие языки программирования (и насколько активно) используются во всех базах кода на протяжении достаточно длительного времени. Сравнив эти данные с графиками с того же сайта Ohloh, я почти сразу решил проверить на этом материале мою идею о выразительности языков программирования. И действительно, я получил очень ожидаемые и при этом на редкость логичные результаты.
Конечно, у этого метода есть слабые места:
- предполагается, что любой коммит используется для добавления единственного самодостаточного фрагмента кода, независимо от того, на каком языке он написан;
- метод не показывает, насколько удобочитаемым получается готовый код (привет, лямбда-выражения!) или сколько времени уходит на их написание, поэтому метод не позволяет судить о поддерживаемости или производительности;
- Ohloh опирается на данные, которые в добровольном порядке присылают сами разработчики свободных проектов, а не собирает информацию самостоятельно. А ведь речь идет об огромном множестве данных, охватывающем около 7,5 миллиона проект-месяцев.
Давайте же обратимся к самим результатам:
Визуализация выполнена при помощи диаграмм типа «ящик с усами», которые эффективно и сравнительно просто показывают распределение числовых данных. Какие числовые данные здесь представлены? Это распределение количества строк кода на коммит ежемесячно, за период около 20 лет, средневзвешенное по количеству коммитов за отдельно взятый месяц. Черная линия, проходящая по центру каждого ящика, — это медиана (50-я процентиль) для данного языка. Языки ранжированы по медиане. Верхняя и нижняя границы ящика — это, соответственно, 25-я и 75-я перцентили, а усы простираются от 10-й до 90-й перцентили. В поле «Total» указана медиана каждого из значений по всем языкам (медиана всех 25-х перцентилей, всех 75-х перцентилей и т.д.), позволяющая составить представление о языке в целом.
Кроме того, я обозначил языки цветом в соответствии с январским рейтингом языков программирования от RedMonk (красным цветом обозначена наиболее популярная группа, синим — вторая по популярности, черным — все остальные). Были рассмотрены лишь относительно распространенные языки.
Какие выводы можно сделать из этой схемы?
Общие характеристики
В принципе, заметные тенденции кажутся логичными. Если сфокусироваться только на языках первой группы, видно, что высокоуровневые языки (Python [#27], Ruby [#34]) демонстрируют высокую выразительность, в то время как более низкоуровневые (C [#50], C++ [#45], Java [#44]) характеризуются многословностью. Аналогично, во второй группе Fortran [#39/#52] и ассемблер [#49] многословны, а «нестарые» функциональные языки занимают средние позиции. Наилучшие результаты у новых функциональных языков.
Языки значительно отличаются по показателю выразительности. Разброс медианных значений — от 48 у Augeas (#1) и 52 у Puppet (#2) до 1629 у Fortran в фиксированном формате (#52). Удивительно, но максимум и минимум различаются в 31 раз.
Для менее выразительных языков характерна гораздо более высокая вариабельность. Наблюдается четкая, но не очень сильная корреляция между медианами (черные линии) и межквартильным размахом (высота ящика). Языки с максимальным межквартильным размахом тяготеют к большим медианам, а языки с равномерной выразительностью также оказываются и более выразительными.
Выразительность языков из первой группы варьируется от плохой до умеренной. Из 11 языков первой группы 5 имеют умеренную выразительность, 6 — плохую. Соотношение LOC/Commit у языков первой группы варьируется от 309 до 1485. Следовательно, отстающие языки в 6—30 раз уступают по выразительности лидирующим. Perl (#26), наиболее выразительный из языков первой группы, в пять раз выразительнее худшего (JavaScript (#51)) и в 3,5 раза выразительнее классического C. Безусловно, это заслуживает внимания, но не может соперничать с примерно 20-кратным повышением LOC/Commit, которого можно достичь при помощи одного из лидирующих языков.
Языки второй группы ровно распределены, многие из них крайне выразительны. Из 52 языков, учтенных в анализе, высокая выразительность характерна примерно до первых 17. Среди них — ни одного языка первой группы, но целых 9 из них относятся ко второй группе. В основном это функциональные языки, за исключением Groovy (#16), Prolog (#13), Puppet (#2) и CoffeeScript (#6).
Языки третьей группы явно тяготеют к высокой выразительности. Из 15 таких языков 8 относятся к первой трети, оставшиеся 7 — ко второй трети. Хотя эти данные не показывают прямой связи между возрастом языка и его выразительностью, можно предположить, что более новые и при этом более выразительные языки начинают терять популярность, и эта тенденция может продолжиться.
Зависимость от класса/типа языка
Функциональные языки обычно очень выразительны. Здесь учтены Haskell (#10), Erlang (#22), F# (#21), диалекты Lisp (в т.ч. Clojure [#7], Emacs Lisp [#14], Dylan [#12], Common Lisp [#23], Scheme [#31] и Racket [#11]), OCaml (#20), R (#17) и Scala (#18). Из всех этих языков лишь два оказались ниже 30 среди 52 рассмотренных.
Языки предметных областей явно тяготеют к высокой выразительности. Augeas (#1), Puppet (#2), R (#17) и Scilab (#19) красноречиво об этом свидетельствуют, лишь VHDL (#38) изолированно расположился в хвосте.
Компилируемость языка не подразумевает снижения выразительности. Я подозревал, что среди самых выразительных языков не окажется ни одного компилируемого, и был неправ. В топ-17 попали следующие компилируемые языки: CoffeeScript (#6), Vala (#9), Haskell (#10) и Dylan (#12).
Интерактивные режимы коррелируют с умеренной выразительностью. Умеренная выразительность характерна для языков с интерактивными оболочками, с несколькими исключениями в плюс и минус. Например: Lisp (#23), Erlang (#22), F# (#21), OCaml (#20), Perl (#26), Python (#27), R (#17), Ruby (#34), Scala (#18), Scheme (#31).
Характеристики отдельных языков
CoffeeScript (#6) оказывается гораздо более выразительным, чем JavaScript (#51), показывая просто превосходный результат. Хотя сама победа CoffeeScript неудивительна, ведь этот язык и создавался ради увеличения выразительности, разница очень высока. Полагаю, что низкая выразительность JavaScript может быть отчасти обусловлена массовым копированием файлов шаблонов, но не отражает качество разработки на самом языке.
Clojure (#7) оказался наиболее выразительным из диалектов Lisp. Существует множество диалектов Lisp, которые в целом ранжируются довольно хорошо. Они перечислены в предыдущем разделе о функциональных языках. Здесь стоит отметить, что лидером среди диалектов Lisp оказался сравнительно распространенный Clojure с медианным значением LOC/commit 101. Далее идут Racket (#11) с 136 и Dylan (#12) с 143.
Среди языков для анализа данных наиболее выразительными оказываются R (#17) и Scilab (#19). R имеет медиану LOC/commit 193, он явный лидер. За R следуют Scilab и Matlab (#35) с медианами 225 и 445 соответственно.
Хотя Go (#24) становится все популярнее, он не отличается особой выразительностью. Постоянно появляется информация о применении Go во все новых стартапах, но по интересующему нас параметру он лишь ненамного превосходит Perl (#26) или Python (#27). Несмотря на это, он обходит по выразительности все языки первой группы, поэтому любой, кто работал с этими языками, скорее всего добьется на Go некоторых улучшений.
А если рассмотреть, насколько регулярно язык проявляет выразительность, не ограничиваясь медианой?
В идеале язык должен быть:
- достаточно прост для изучения, чтобы абсолютное большинство работающих с ним программистов могли выдавать высокую продуктивность;
- демонстрировать равную выразительность практически во всем спектре его применения.
Чтобы выполнить такие измерения, рассмотрим межквартильный размах (IQR; это разница между 25-й и 75-й перцентилями) и распределим языки по этому показателю:
Здесь нас интересует высота ящиков. Слева ящики совсем маленькие — наилучший показатель в 23 строки у CoffeeScript. Далее высота увеличивается, список замыкает фортран с фиксированным форматом с 1854 строками.
Вот несколько замечаний по этому графику, на которые стоит обратить внимание, прежде чем сравнить два графика:
- как было указано выше и дополнительно проиллюстрировано здесь, неравномерность коррелирует с многословностью, а равномерность — с выразительностью;
- здесь языки первой группы показывают себя гораздо лучше, четыре из них попадают в первую треть (Python #11, Objective-C #13, Perl #15 и C# #17). Shell почти попадает в группу лидеров, оказавшись под номером 19. Показатели межквартильного размаха этих языков варьируются от 90 до 167 LOC/commit, даже в группе лидеров это довольно большая разница;
- следовательно, языки третьей группы здесь проседают, хотя по признаку выразительности показывали исключительно высокие результаты;
- с учетом обоих параметров Java характеризуется наивысшей производительностью среди языков «для предприятий» (C, C++, Java). По выразительности Java практически не уступает C++ (оба на уровне 823 LOC/commit), но отличается значительно большей равномерностью (межквартильный размах 277 против 476);
- CoffeeScript — язык #1 по равномерности, его межквартильный интервал составляет всего 23 LOC/commit, тогда как #4 — Clojure имеет уже 51 LOC/commit. У языка #8 Groovy межквартильный размах составляет уже 68 на LOC/commit. Иными словами, CoffeeScript обладает исключительно равномерной выразительностью во всех областях применения и в коде, написанном разными разработчиками;
- очень интересны отклонения — языки с необычно высокими или низкими медианами по сравнению с языками-соседями. Если медиана сравнительно высокая, то перед нами необычно равномерный, но не очень выразительный язык. В противном случае язык сравнительно неравномерен (это означает, что при такой выразительности он должен был бы располагаться на схеме левее);
- языки первой группы отличаются высокой равномерностью независимо от их выразительности. Практически во всех случаях их медианы выше, чем у соседей — то есть они явно сдвинуты влево от ожидаемого местоположения. Из этого следует вывод, что основной чертой языков первой группы является даже не продуктивность, а высокая предсказуемость;
- напротив, большинство языков, которые, казалось бы, сдвинуты вправо, — это языки третьей группы. Недостаток предсказуемости не позволяет им попасть даже во вторую группу.
Итак, какие же языки являются лучшими по этим параметрам?
Если выбрать топ-10 языков, ранжированных по медианному значению, а затем — топ-10 по межквартильному размаху, и сравнить их, вот что получится. Медианное значение и межквартильный размах указаны сразу после названия языка:
- Augeas (48, 28): предметно-ориентированный язык для конфигурационных файлов;
- Puppet (52, 65): еще один конфигурационный предметно-ориентированный язык;
- REBOL (57, 47): язык, разработанный для распределенных вычислений;
- eC (75, 75): Ecere C, производный от C с объектной ориентацией;
- CoffeeScript (100, 23): высокоуровневый язык, транскомпилируемый в JavaScript;
- Clojure (101, 51): диалект Lisp для функционального параллельного программирования;
- Vala (123, 61): объектно-ориентированный язык, используемый в GNOME;
- Haskell (127, 71): чисто функциональный компилируемый язык с сильной статической типизацией.
Вернувшись к диаграмме «ящик с усами», я бы убрал из группы лидеров eC, поскольку он имеет плохую производительность. Этот язык оказывается очень неравномерным как минимум в четверти случаев (обратите внимание на 75-ю перцентиль). Кроме того, я исключил бы Augeas и Puppet, поскольку оба они являются предметно-ориентированными.
Скомбинировав эти данные с информацией RedMonk о ранжировании языков по популярности, приходим к выводу, что в первых двух группах популярности есть всего три очень выразительных универсальных языка:
- Clojure;
- CoffeeScript;
- Haskell.
Если вы задумываетесь об изучении нового языка, то было бы очень разумно взять на заметку Clojure, CoffeeScript и Haskell, так как они не только выразительны, но и используются в достаточно широких сообществах, которые кажутся стабильными (предсказуемыми).
Ни один из языков первой группы не попадает в топ-25 по двум показателям. Из языков первой группы более низкоуровневые оказываются и более неравномерными, и более многословными, тогда как для высокоуровневых характерна умеренная пространность и очень высокая равномерность. Наиболее равномерные языки — это Python, Objective-C, Perl, C# и shell. Наличие Perl и shell в этом списке подкрепляет исходное предположение о том, что выразительность языка почти не связана с его удобочитаемостью и удобством поддержки. Ruby — интересный язык, «нарушающий закономерности» соотношения выразительности и равномерности, наблюдаемые у других высокоуровневых языков. Возможно, дело в фреймворке (Rails), очень популяризующем язык, который в противном случае не был бы так успешен.
Если для вашего проекта требуется выразительный язык, по которому сравнительно несложно найти специалистов, обратите пристальное внимание на Python. Из языков первой группы наиболее впечатляющими общими показателями обладают Python, Perl, Shell и Objective-C. Я считаю Python самым перспективным из этих универсальных языков. По-моему, весьма целесообразно применять в проектах многоязычное программирование и писать настолько высокоуровневый код, насколько допускают поставленные требования. К счастью, многие высокоуровневые языки (в том числе Python) позволяют применять модули, основанные на более производительных языках, например, на C. Таким образом, вы можете реализовать большую часть проекта на более продуктивном и выразительном языке, который будет уступать место более высокопроизводительным языкам в случае необходимости.
Донни Беркхольц
Релоцировались? Теперь вы можете комментировать без верификации аккаунта.