В последнем семестре университета я выбрал курс компиляторов CS444. Там каждая группа из 1-3 человек должна была написать компилятор из существенного подмножества Java в x86. Язык на выбор группы. Это была редкая возможность сравнить реализации больших программ одинаковой функциональности, написанных очень компетентными программистами на разных языках, и сравнить разницу в дизайне и выборе языка. Такое сравнение породило массу интересных мыслей. Редко можно встретить такое контролируемое сравнение языков. Оно не идеально, но намного лучше, чем большинство субъективных историй, на которых основано мнение людей о языках программирования.

Мы сделали наш компилятор на Rust, и сначала я сравнил его с проектом команды на Haskell. Я ожидал, что их программа будет намного короче, но она оказалась того же размера или больше. То же самое для OCaml. Затем сравнил с компилятором на C++, и там вполне ожидаемо компилятор был примерно на 30% больше, в основном, из-за заголовков, отсутствия типов sum и сопоставлений с образцом. Следующее сравнение было с моей подругой, которая сделала компилятор самостоятельно на Python и использовала менее половины кода, по сравнению с нами, из-за мощности метапрограммирования и динамических типов. У другого товарища программа на Scala тоже была меньше нашей. Больше всего меня удивило сравнение с другой командой, которая тоже использовала Rust, но у них оказалось в три раза больше кода из-за разных дизайнерских решений. В конце концов, самая большая разница в количестве кода оказалась в пределах одного языка!

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

Содержание


  • Почему я считаю это содержательным
  • Rust (основа для сравнения)
  • Haskell: 1,0-1,6 размера в зависимости от того, как считать, по интересным причинам
  • C++: 1,4 размера по очевидным причинам
  • Python: 0,5 размера из-за причудливого метапрограммирования!
  • Rust (другая группа): трёхкратный размер из-за иного дизайна!
  • Scala: 0,7 размера
  • OCaml: 1,0-1,6 размера в зависимости от того, как считать, похоже на Haskell

Почему я считаю это содержательным


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

  • Никто (включая меня) не знал, что я буду измерять этот параметр, поэтому никто не пытался играть в метрики, все просто пытались закончить проект быстро и правильно.
  • Все (за исключением проекта Python, о котором я расскажу позже) реализовывали программу с единственной целью пройти один и тот же автоматизированный набор тестов в одни и те же сроки, поэтому результаты не могут быть сильно искажены группами, решающими разные проблемы.
  • Проект был выполнен в течение нескольких месяцев, с командой, и должен был постепенно расширяться и проходить как известные, так и неизвестные тесты. Это означает, что было полезно писать чистый понятный код.
  • Кроме прохождения тестов курса, код не будет использоваться ни для чего другого, никто не будет его читать и, будучи компилятором для ограниченного подмножества Java в текстовый ассемблер, он не будет полезен.
  • Никакие библиотеки, кроме стандартной библиотеки, не разрешены, и никакие хелперы для парсинга, даже если они есть в стандартной библиотеке. Это означает, что сравнение не может быть искажено мощными библиотеками компилятора, которые есть только у некоторых команд.
  • Были не только публичные, но и секретные тесты. Они запускались один раз после окончательной сдачи. Это означало, что был стимул написать свой собственный тестовый код и убедиться, что компилятор надёжный, правильный и обрабатывает сложные пограничные ситуации.
  • Хотя все участники — студенты, но я считаю их вполне компетентными программистами. Каждый по крайней мере два года проходил стажировку, в основном, в высокотехнологичных компаниях, иногда даже работал над компиляторами. Почти все они программируют в течение 7-13 лет и являются энтузиастами, которые много читают в интернете за рамками своих курсов.
  • Сгенерированный код не учитывался, но учитывались файлы грамматики и код, который генерировал другой код.

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

Rust (основа для сравнения)


Я и один из моих товарищей раньше каждый написал более 10k строк на Rust, а третий коллега написал, возможно, 500 строк на каких-то хакатонах. Наш компилятор вышел в 6806 строк wc -l, 5900 строк исходника (без пробелов и комментариев), и 220 КБ wc -c.

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

Я написал другую статью с описанием нашего дизайна, который прошёл все публичные и секретные тесты. Он также содержит несколько дополнительных функций, которые мы сделали для удовольствия, а не для прохождения тестов, что, вероятно, добавило около 400 строк. Также в нём около 500 строк наших модульных тестов.

Haskell


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

Их компилятор составил 9750 строк по wc -l, 357 КБ и 7777 строк кода (SLOC). У этой команды также единственные существенные различия между этими соотношениями: их компилятор в 1,4 раза больше нашего по строкам, в 1,3 раза по SLOC и в 1,6 раза по байтам. Они не реализовали никаких дополнительных функций, прошли 100% публичных и секретных тестов.

Важно отметить, что включение тестов больше всего сказалось именно на этой команде. Поскольку они тщательно подошли к правильности кода, то включили 1600 строк тестов. Они поймали несколько пограничных ситуаций, которые наша команда не отловила, но просто эти случаи не проверялись тестами курса. Так что без тестов с обеих сторон (6,3 тыс. строк против 8,1 тыс. строк) их компилятор всего на 30% больше нашего.

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

Покопавшись с одним из моих друзей по команде, мы придумали следующее объяснение этой разнице:

  • Мы использовали рукописный лексический анализатор и метод рекурсивного спуска, а они — генератор на NFA и DFA и LR-парсер, а затем проход для преобразования дерева синтаксического анализа в AST (абстрактное синтаксическое дерево, более удобное представление кода). Это дало им существенно больше кода: 2677 строк по сравнению с нашим 1705, примерно на 1000 строк больше.
  • Они использовали причудливый генерик AST, который переходил к различным параметрам типа, поскольку больше информации добавлялось в каждом проходе. Это и больше вспомогательных функций для перезаписи, вероятно, объясняют, почему их код AST примерно на 500 строк больше, чем наша реализация, где мы собираем с литералами struct и мутируем поля Option<_> для добавления информации по мере прохождения.
  • У них ещё около 400 строк кода при генерации, которые в основном связаны с большей абстракцией, необходимой для генерации и объединения кода чисто функциональным способом, где мы просто используем мутацию и запись строк.

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

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

Поклонники Haskell возразят, что та команда, вероятно, не использовала в полной мере возможности Haskell, и если бы они лучше знали язык, то могли бы сделать проект с меньшим количеством кода. Согласен, кто-то вроде Эдварда Кметта может написать тот же компилятор в значительно меньшем объёме. Действительно, команда моего друга не использовала много причудливых суперпродвинутых абстракций и причудливых библиотек комбинаторов, таких как lens. Однако всё это сказывается на читаемости кода. Все люди в команде — опытные программисты, они знали, что Haskell способен на очень причудливые вещи, но решили не использовать их, потому что решили, что на их понимание потребуется больше времени, чем они сэкономят, и сделает код сложнее для понимания другими. Это кажется мне реальным компромиссом, а утверждение, что Haskell волшебно подходит для компиляторов, переходит в нечто вроде «Haskell требует чрезвычайно высокой квалификации для написания компиляторов, если вы не заботитесь о поддержке кода людьми, которые также не очень искусны в Haskell».

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

С++


Затем я поговорил с моим другом из команды C++. Я знал только одного человека в этой команде, но C++ используется на нескольких курсах в нашем университете, поэтому, вероятно, у всех в команде был опыт C++.

Их проект состоял из 8733 строк и 280 КБ, не включая тестовый код, но включая около 500 строк дополнительных функций. Что делает его в 1,4 раза больше нашего кода без тестов, у которого тоже около 500 строк дополнительных функций. Они прошли 100% публичных тестов, но только 90% секретных тестов. Предположительно потому, что они не реализовали причудливые массивы vtables, требуемые спецификацией, которые занимают, возможно, 50-100 строк кода.

Я не очень глубоко копался в этих различиях по размеру. Предполагаю, это в основном объясняется:

  • Они используют LR-парсер и рерайтер деревьев (tree rewriter) вместо метод рекурсивного спуска.
  • Отсутствие типов sum и сопоставлений с образцом в C++, которые мы широко использовали и которые были очень полезны.
  • Необходимость дублировать все сигнатуры в заголовочных файлах, чего нет в Rust.

Ещё мы сравнивали время компиляции. На моём ноутбуке чистая отладочная сборка нашего компилятора занимает 9,7 с, чистый релиз 12,5 с, а инкрементная отладочная сборка — 3,5 с. У моего друга не было таймингов под рукой для их билда C++ (используя parallel make), но он сказал, что цифры похожие, с оговоркой, что они помещают реализации множества небольших функций в заголовочные файлы, чтобы уменьшить дублирование сигнатур ценой более длительного времени (именно поэтому я не могу измерить чистые накладные расходы на строки в заголовочных файлах).

Python


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

Её компилятор состоял из 4581 строки и прошёл все публичные и секретные тесты. Она также реализовала больше дополнительных функций, чем любая другая команда, но трудно определить, сколько дополнительного кода это заняло, потому что многие из дополнительных функций были более мощными версиями простых вещей, которые всем нужно было реализовать, таких как свёртывание констант и генерация кода. Дополнительные функции, вероятно, составляют 1000-2000 строк, по крайней мере, поэтому я уверен, что её код по крайней мере вдвое выразительнее нашего.

Одна большая часть этого различия, вероятно, динамическая типизация. Только в нашем ast.rs 500 строк определений типов, и ещё много типов, определённых в других местах компилятора. Мы также всегда ограничены самой системой типов. Например, нам нужна инфраструктура для эргономичного добавления новой информации в AST по мере прохождения и доступа к ней позже. В то время как в Python вы можете просто установить новые поля на узлах AST.

Мощное метапрограммирование также объясняет часть разницы. Например, хотя она использовала LR-парсер вместо метода рекурсивного спуска, в её случае я думаю, что он занял меньше кода, потому что вместо прохода с перезаписью дерева её грамматика LR включала фрагменты кода Python для построения AST, которые генератор мог превратить в функции Python с помощью eval. Часть причины, по которой мы не использовали LR-парсер, заключается в том, что построение AST без перезаписи дерева потребует много церемоний (создание либо файлов Rust, либо процедурных макросов), чтобы связать грамматику с фрагментами кода Rust.

Другой пример мощи метапрограммирования и динамической типизации — 400-строчный файл visit.rs, это в основном повторяющийся шаблонный код, реализующий визитора на куче структур AST. В Python это может быть короткая функция примерно на 10 строк, которая рекурсивно интроспектирует поля узла AST и посещает их (используя атрибут __dict__).

Как поклонник Rust и статически типизированных языков в целом, я склонен отметить, что система типов очень полезна для предотвращения ошибок и для производительности. Необычное метапрограммирование также может затруднить понимание того, как работает код. Однако это сравнение удивило меня тем, что я не ожидал, что разница в количестве кода будет настолько большой. Если разница в целом действительно близка к необходимости писать в два раза больше кода, я все ещё думаю, что Rust подходящий компромисс, но всё-таки в два раза меньше кода — аргумент, и в будущем я склонен что-то сделать на Ruby/Python, если нужно просто быстро что-то построить в одиночку, а затем выбросить.

Rust (другая группа)


Cамое интересное для меня сравнение было с моим другом, который тоже делал проект в Rust с одним товарищем по команде (которого я не знал). У моего друга был хороший опыт Rust. Он внёс вклад в разработку компилятора Rust и много читал. О его товарище ничего не знаю.

Их проект состоял из 17 211 необработанных строк, 15k исходных строк и 637 КБ, не включая тестовый код и сгенерированный код. У него не было дополнительных функций, и он прошёл только 4 из 10 секретных тестов и 90% публичных тестов на генерацию кода, потому что у них не хватило времени перед дедлайном для реализации более причудливых частей спецификации. Их программа в три раза больше нашей, написана на том же языке, и с меньшей функциональностью!

Этот результат был действительно удивителен для меня и затмевал все различия между языками, которые я исследовал до сих пор. Поэтому мы сравнили списки размеров файлов wc -l, а также проверили, как каждый из нас реализовал некоторые конкретные вещи, которые вылились в разный размер кода.

Похоже, всё сводится к последовательному принятию различных дизайнерских решений. Например, их фронтенд (лексический анализ, синтаксический анализ, построение AST) занимает 7597 строк против наших 2164. Они использовали лексический анализатор на DFA и синтаксический анализатор LALR(1), но другие группы делали подобные вещи без такого количества кода. Глядя на их weeder-файл, я заметил ряд дизайнерских решений, которые отличались от наших:

  • Они решили использовать полностью типизированное дерево синтаксического анализа вместо стандартного однородного дерева синтаксического анализа на основе строк. Вероятно, это потребовало намного больше определений типов и дополнительного кода преобразования на этапе синтаксического анализа или более сложного генератора синтаксического анализа.
  • Они использовали реализации трейтов tryfrom для преобразования между типами дерева синтаксического анализа и типами AST при проверке их правильности. Это приводит к множеству 10-20-строчных блоков impl. Мы для этого использовали функции, возвращающие типы Result, что генерирует меньше строк, а также немного освобождает нас от структуры типов, упрощая параметризацию и повторное использование. Некоторые вещи, которые для нас были однострочными ветвями match, у них были представляли собой 10-строчные блоки impl.
  • Наши типы структурированы так, чтобы уменьшить копипаст. Например, они использовали отдельные поля is_abstract, is_native и is_static, где код проверки ограничений необходимо было скопировать дважды: один раз для void-типизированных методов и один раз для методов с типом return, с небольшими изменениями. В то время как у нас void был просто специальным типом, и мы придумали таксономию модификаторов с mode и visibility, которые применяли ограничения на уровне типа, а ошибки ограничений были сгенерированы по умолчанию для оператора match, который перевёл наборы модификаторов в mode и visibility.

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

Их часть для генерации кода состоит из 3594 строк, а наша — 1560. Я посмотрел на их код и кажется, что почти вся разница в том, что они выбрали промежуточную структуру данных для инструкций ассемблера, где мы просто использовали форматирование строк для прямого вывода ассемблера. Им пришлось определять типы и функции вывода для всех используемых инструкций и типов операндов. Это также означало, что построение инструкций ассемблера заняло больше кода. Где у нас был оператор форматирования с короткими инструкциями, такими как mov ecx, [edx], им нужен был гигантский оператор rustfmt, разбитый на 6 строк, которые строили инструкцию с кучей промежуточных вложенных типов для операндов, включающих до 6 уровней вложенных скобок. Мы также могли выводить блоки связанных инструкций, таких как преамбула функции, в одном операторе форматирования, где им пришлось указывать полную конструкцию для каждой инструкции.

Наша команда рассматривала возможность использования такой абстракции, как у них. Было проще иметь возможность либо выводить текстовую сборку, либо непосредственно выдавать машинный код, однако это не являлось требованием курса. То же самое можно было сделать с меньшим количеством кода и лучшей производительностью, используя трейт X86Writer с методами вроде push(reg: Register). Мы учитывали также, что это могло бы упростить отладку и тестирование, но мы поняли, что просмотр сгенерированного текстового ассемблера на самом деле легче читать и тестировать с помощью тестирования моментальных снимков, если либерально вставлять комментарии. Но мы (видимо, правильно) предсказали, что это займёт много дополнительного кода, и не было никакой реальной выгоды, учитывая наши реальные потребности, поэтому мы не беспокоились.

Хорошо сравнить это с промежуточным представлением, которое команда C++ использовала в качестве дополнительной функции, что отняло у них всего 500 дополнительных строк. Они использовали очень простую структуру (для простых определений типов и кода построения), которая использовала операции, близкие к тому, что требовала Java. Это означало, что их промежуточное представление было намного меньше (и, следовательно, требовало меньше кода построения), чем результирующий ассемблер, поскольку многие языковые операции, такие как вызовы и приведения, расширялись во многие ассемблерные инструкции. Они также говорят, что это действительно помогло отладке, так как вырезало много мусора и улучшала читаемость. Представление более высокого уровня также позволило сделать некоторые простые оптимизации на их промежуточном представлении. Команда C++ придумала действительно хороший дизайн, который принёс им гораздо больше пользы с гораздо меньшим количеством кода.

В целом, похоже, что общая причина трёхкратной разницы в объёме обусловлен последовательным принятием различных проектных решений как больших, так и малых в направлении большего количества кода. Они реализовали ряд абстракций, которые мы не сделали — они добавили больше кода, и пропустили некоторые из наших абстракций, которые уменьшают количество кода.

Этот результат действительно удивил меня. Я знал, что дизайнерские решения имеют значение, но я бы не догадался заранее, что они приведут к каким-либо различиям такого размера, учитывая, что я только обследовал людей, которых я считаю сильными компетентными программистами. Из всех результатов сравнения это самый значительный для меня. Наверное, мне помогло, что я много читал о том, как писать компиляторы, прежде чем пошёл на этот курс, поэтому мог воспользоваться умными проектами, которые другие люди придумали и нашли хорошо работающими, вроде визиторов AST и метода рекурсивного спуска, хотя их не преподавали у нас на курсе.

Что действительно заставило меня задуматься — это стоимость абстракции. Абстракции могут облегчить расширение в будущем или защитить от некоторых типов ошибок, но их нужно учитывать с учётом того, что вы можете получить в три раза больше кода для понимания и рефакторинга, в три раза больше возможных мест для ошибок и меньше времени на тестирование и дальнейшую разработку. Наш учебный курс отличался от реального мира: мы точно знали, что никогда не коснёмся кода после разработки, это исключает преимущества упреждающей абстракции. Однако, если мне пришлось бы выбрать, какой компилятор расширить произвольной функцией, которую вы скажете позже, я бы выбрал наш, даже не учитывая своё знакомство с ним. Просто потому что в нём намного меньше кода, который нужно понять, и я мог бы потенциально выбрать лучшую абстракцию для требований (например, промежуточное представление команды C++), когда буду знать конкретные требования.

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

Scala


Я также поговорил с другом, который в предыдущем семестре делал проект на Scala, но проект и тесты были точно такими же. Их компилятор состоял из 4141 строки и ~160 КБ кода, не считая тестов. Они прошли 8 из 10 секретных тестов и 100% открытых тестов и не реализовали никаких дополнительных функций. Таким образом, по сравнению с нашими 5906 строками без дополнительных функций и тестов, их компилятор меньше на 30%.

Одним из факторов дизайна в малого объёма стал другой подход к парсингу. Курс позволял использовать инструмент командной строки для генератора таблиц LR. Его никто не использовал, кроме этой команды. Это избавило их от необходимости реализовывать генератор таблиц LR. Им также удалось избежать написания грамматики LR с помощью 150-строчного скрипта Python, который скрапил веб-страницу грамматики Java, которую они нашли в интернете, и переводил её во входной формат генератора. Им всё ещё нужно было сделать какое-то дерево в Scala, но в целом этап парсинга составил 1073 строк по сравнению с нашими 1443, хотя наш метод градиентного спуска здесь давал преимущество по объёму по сравнению со всеми другими командами.

Остальная часть их компилятора была тоже меньше нашей, без каких-то очевидных больших различий в дизайне, хотя я не копался в коде. Подозреваю, что это связано с различиями в выразительности Scala и Rust. Scala и Rust имеют схожие функциональные возможности программирования, полезные для компиляторов, такие как сопоставление шаблонов, но управляемая память Scala экономит код, необходимый для работы borrow checker в Rust. Кроме того, в Scala более разнообразный синтаксический сахар, чем в Rust.

OCaml


Поскольку все члены нашей команды проходят стажировку в Jane Street (технологическая компания вычислительного трейдинга — прим. пер.), мне было особенно интересно посмотреть на результат других бывших стажёров Jane Street, которые выбрали для написания компилятора язык OCaml.

Их компилятор составлял 10914 строк и 377 КБ, включая небольшое количество тестового кода и никаких дополнительных функций. Они прошли 9/10 секретных тестов и все публичные тесты.

Как и другие группы, похоже, что основная разница в размерах связана с использованием для синтаксического анализа LR-парсера и перезаписи дерева, а также конвейера преобразования regex->NFA->DFA для лексического анализа. Их фронтенд (лексический анализ, синтаксический анализ, конструкция AST) составляет 5548 строк, а наш — 2164, с аналогичными соотношениями для байтов. Они также использовали для своего парсера тестирование с ожиданием, что похоже на наши snapshot-тесты, которые помещают ожидаемый вывод за пределы кода, поэтому их тесты парсера составляли ~600 строк из общего числа, а наши — около 200.

Это оставляет для остальной части компилятора 5366 строк у них (461 строка из которых — интерфейсные файлы с объявлениями типов) и 4642 у нас, разница всего на 15%, если считать их интерфейсные файлы, и практически одинаковый размер, если не считать. Похоже, что, не считая наших дизайнерских решений по парсингу, Rust и OCaml кажутся одинаково выразительными, за исключением того, что OCaml нужны интерфейсные файлы, а Rust — нет.

Заключение


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

Комментарии (73)


  1. smer44
    18.06.2019 19:52
    +1

    В последнем семестре университета я выбрал курс компиляторов CS444. Там каждая группа из 1-3 человек должна была написать компилятор из существенного подмножества Java в x86.

    а можно поподробнее о задаче, какие там точно действия после AST, желательно на русском, может и я на питоне что то накалякаю, сравним. Всё думаю о написании универсального компилятора, с контролем юзера над действиями и типом результата…

    Её компилятор состоял из 4581 строки

    для питона — гигантский проэкт, может там всё таки имеется флуд?
    кстати с eval нечестно, но прелесть питона в том что там за строк 10 пишется свой eval

    Другой пример мощи метапрограммирования и динамической типизации — 400-строчный файл visit.rs, это в основном повторяющийся шаблонный код, реализующий визитора на куче структур AST. В Python это может быть короткая функция примерно на 10 строк, которая рекурсивно интроспектирует поля узла AST и посещает их (используя атрибут __dict__).

    А разве нельзя в другом языке сделать стирание типов например один класс ASTNode, с присваиванием функций даже java 8 там может, вместо того чтоб расписывать флуд, чем являются паттерны програмирования

    Как поклонник Rust и статически типизированных языков в целом, я склонен отметить, что система типов очень полезна для предотвращения ошибок и для производительности.

    Проверять ошибки можно, а при непредсказуемой структуре нужно в рантайме, в python это элегантно делается декоратором, при этом вы имеете приимущество встраивания любых своих проверок и возможностью включать и выключать по желанию. Собсветнно я сторонник включения / выключения проверки типов во желанию.


    1. Uhehesh
      18.06.2019 20:27
      +1

      Это перевод, так что вряд ли вам расскажут подробнее. :)


      1. vdonich
        19.06.2019 21:04

        вряд ли вам расскажут подробнее

        На странице оригинала указаны контакты автора, так что можно попробовать обратиться напрямую.


    1. Gray5
      20.06.2019 07:46

      По ссылке на курс компиляторов CS444 можно больше о задании, например:

      The overall project for the course is to write a compiler from Joos 1W, a fairly large subset of Java, to i386 assembly language (the Netwide Assembler dialect).

      Возможно даже можно будет найти открытые тесты.


  1. Serge78rus
    18.06.2019 20:31
    +4

    Очень интересно было бы еще увидеть сравнение производительности полученных компиляторов.


    1. smer44
      20.06.2019 00:07

      главное не производительность а качество кода. А так на дворе 21 век так что можно задуматься можно ли сделать компилятор на паралельной платформе например CUDA какой нить.


  1. longtolik
    18.06.2019 21:46

    А длины строк не сравнивали? Или, может быть, размер по диагонали, скажем, программа на Python диагональю 52".


    1. Prototik
      19.06.2019 16:58
      +2

      Т.е. самый оптимальный код в таком случае — "квадрат"?


  1. lostmsu
    18.06.2019 22:08

    Есть же the benchmarks game.


    1. QtRoS
      18.06.2019 22:56
      +1

      Там больше про скорость, тут про дизайн.


  1. 0xd34df00d
    18.06.2019 22:24
    +2

    По моему опыту с хаскелем вещи типа lens, uniplate/geniplate и паттерны типа trees that grow сокращают объём кода минимум на порядок по сравнению с теми же плюсами.


    Жалко, что исходников нет.


  1. twinklede
    19.06.2019 09:01

    Необходимость дублировать все сигнатуры в заголовочных файлах, чего нет в Rust.

    Это неверно. Такой необходимости нет.


    1. twinklede
      19.06.2019 10:37
      +1

      Вот и объективная критика пожаловала. Именно так она выглядит.


    1. Dark_Daiver
      19.06.2019 11:51
      +2

      (минус не мой)
      Необходимости конечно нет, но разбиение на реализацию/объявление заметно улучшает читаемость, и ускоряет компиляцию. Это слишком серьезные преимущества, чтоб игнорировать их в проекте на несколько тысяч строк.


      1. twinklede
        19.06.2019 11:58

        Необходимости конечно нет, но разбиение на реализацию/объявление заметно улучшает читаемость, и ускоряет компиляцию.

        А чего же их не разделяют в rust? Да и много где ещё. Подобные рассуждения не работают, вообще. Вы либо последовательно применяйте это ко всем языкам, либо не применяете вообще.
        заметно улучшает читаемость

        Это догмат. Лично для меня это ничего и никак не улучшает, как и для пользователей тысяч других языков, где нет этого разделение. К тому же не каждый С++-код можно разделить, а современный С++ разделить вообще почти нельзя.

        и ускоряет компиляцию.

        Это попросту глупость разоблачённая уже давно. Как-то ускорение есть лишь в очень редких случаях, либо на очень большой кодовой базе. Вернее даже не ускорение, а просто возможность распараллелить сборку.

        К тому же, такого разделение в rust нет и там собирается всё модульно. Опять же, либо будьте последовательны и применяйте это ко всем языкам, либо не применяйте вообще.
        Это слишком серьезные преимущества, чтоб игнорировать их в проекте на несколько тысяч строк.

        Повторю, они игнорируются в «несколько тысяч строк на rust» и проблем не вызывают, как и в тысячах других языков.


        1. Dark_Daiver
          19.06.2019 12:12

          Если что, я не то чтобы топлю за Rust. Сам пишу на С++ и люблю его умеренной любовью.

          А чего же их не разделяют в rust? Да и много где ещё. Подобные рассуждения не работают, вообще. Вы либо последовательно применяйте это ко всем языкам, либо не применяете вообще.

          Не знаю :) Это вопрос к авторам дизайна языка.
          Это догмат. Лично для меня это ничего и никак не улучшает, как и для пользователей тысяч других языков, где нет этого разделение.

          На мой субъективный взгляд, при чтении кода некоторого модуля, проще сначала прикинуть что в этом модуле вообще находится, и только потом переходить к чтению реализаций. Читать протянку из смеси объявлений и реализаций не очень удобно. Эта проблема частично решается умными IDE в которых есть folding реализаций, конечно.
          Да это мое субъективное мнение.

          современный С++ разделить вообще почти нельзя.

          А можно пример?

          Это попросту глупость разоблачённая уже давно.

          Было бы здорово увидеть пример.
          Как-то ускорение есть лишь в очень редких случаях, либо на очень большой кодовой базе. Вернее даже не ускорение, а просто возможность распараллелить сборку.

          Я не то чтобы спец, но предположим у вас в реализации функции не какая-нибудь банальщина, а инстанцирование тяжелых линейных солверов из Eigen, приправленных expression templates. В этом случае раздельная компиляция просто жизненно необходима. И это не то чтобы прям редкий случай, заметная доля проектов с С++ это числодробилки, в которых постоянно возникает подобная ситуация.


          1. twinklede
            19.06.2019 12:43

            Если что, я не то чтобы топлю за Rust. Сам пишу на С++ и люблю его умеренной любовью.

            Ну ваши претензии не работают в рамках логики автора, да и в моей тоже.

            А можно пример?

            Всё что связано с шаблонами. Разделять то же auto не имеет смысла, т.к. типы по-сути неизвестен. Много примеров.

            Было бы здорово увидеть пример.

            chromium.googlesource.com/chromium/src/+/lkgr/docs/jumbo.md

            Я не то чтобы спец, но предположим у вас в реализации функции не какая-нибудь банальщина, а инстанцирование тяжелых линейных солверов из Eigen, приправленных expression templates. В этом случае раздельная компиляция просто жизненно необходима. И это не то чтобы прям редкий случай, заметная доля проектов с С++ это числодробилки, в которых постоянно возникает подобная ситуация.

            Вам это никак не поможет. Это поможет только в том случае, если вы обернёте шаблонное api в c-api и уже с ним будете работать. Но будет уже не С++. Если же ваша логика будет так же шаблонной и так же использовать какую-то логику из библиотеки в cpp-файле, то это ничего не даст.

            В современном С++-коде код не разбивается(в силу нежелания этого делать и в силу невозможности этого сделать). Существует тысячи ho-библиотек и тот же stdlib так же является ho-библиотекой.

            Таким образом в каждом вашем cpp-файле будет 1% вашего кода и 99% заинклюженого. Таким образом компилятор будет делать лишнюю работу, собирая тысячи раз одно и те же инклюды.

            Просто для понимания сделайте подобный бенчмарк:

            $ cat a.cpp 
            #include <iostream>
            
            void a() {
              std::cout << __PRETTY_FUNCTION__ << std::endl;
            }
            $ cat b.cpp 
            #include <iostream>
            
            void b() {
              std::cout << __PRETTY_FUNCTION__ << std::endl;
            }
            $ cat c.cpp 
            #include <iostream>
            
            void c() {
              std::cout << __PRETTY_FUNCTION__ << std::endl;
            }
            $ cat main.cpp 
            #ifdef HO
            #include "a.cpp"
            #include "b.cpp"
            #include "c.cpp"
            #else
            void a(); void b(); void c(); 
            #endif
            
            int main() {
                a(); b(); c();
            }
            
            $ time g++ a.cpp b.cpp c.cpp main.cpp 
            
            real    0m0,466s
            user    0m0,398s
            sys     0m0,068s
            
            $ time g++  main.cpp -DHO
            
            real    0m0,178s
            user    0m0,152s
            sys     0m0,026s
            
            
            $ time clang++  main.cpp -DHO 
            
            real    0m0,684s
            user    0m0,589s
            sys     0m0,046s
            
            $ time clang++ a.cpp b.cpp c.cpp main.cpp
            
            real    0m1,831s
            user    0m1,721s
            sys     0m0,100s


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


            1. Dark_Daiver
              19.06.2019 12:59

              Ну ваши претензии не работают в рамках логики автора, да и в моей тоже.

              Ну у меня все началось не с претензий, а с попытки обосновать, почему в production ready c++ коде хорошо разделять объявления и реализацию.

              chromium.googlesource.com/chromium/src/+/lkgr/docs/jumbo.md

              Прикольно, почитаю, спасибо.

              Всё что связано с шаблонами. Разделять то же auto не имеет смысла, т.к. типы по-сути неизвестен. Много примеров.

              Если вы про auto в сигнатуре, то лично я бы предпочел видеть там полноценный тип/шаблон. Да, не во всех ситуациях это будет красиво, но в большинстве — снимет кучу головной боли при чтении кода.
              Да, шаблоны вполне себе разбиваются. Не на .h и .cpp, конечно, но на блоки объявления и реализации — вполне.

              Вам это никак не поможет. Это поможет только в том случае, если вы обернёте шаблонное api в c-api и уже с ним будете работать. Но будет уже не С++.

              Я про случаи когда
              «MyFuncs.h»
              Eigen::VectorXf someHeavyFunction(const Eigen::VectorXf &someArg);

              «MyFuncs.cpp»
              
              Eigen::VectorXf someHeavyFunction(const Eigen::VectorXf &someArg)
              {
                  // Put some heavy template stuff here
              }

              Вполне себе С++, вполне себе хороший способ убрать компиляцию тяжелых частей в локализованные .cpp. Кроме прочего, таким образом можно уменьшить количество инклудов которые расползутся по проекту, ставя их только в .cpp

              Пример интересный, спасибо, но не уверен что все это так же хорошо масштабируется на относительно большие проекты


              1. twinklede
                19.06.2019 13:18
                -1

                Если вы про auto в сигнатуре, то лично я бы предпочел видеть там полноценный тип/шаблон. Да, не во всех ситуациях это будет красиво, но в большинстве — снимет кучу головной боли при чтении кода.
                Да, шаблоны вполне себе разбиваются. Не на .h и .cpp, конечно, но на блоки объявления и реализации — вполне.

                Это будет некрасиво в случае использования современного С++-кода. Современный С++-код полагается на типизацию и сложные типы, которые не так-то просто вывести руками. И именно auto убирает все эти проблемы.

                К тому же подобный код ненужно читать. 1min / 1s — вы же не будите руками писать тип? И вам ненужно при чтении знать тип. Задача системы типов сделать таким образом, что-бы вы не смогли написать 1min * 1s;

                К тому же, выводит типы задача ide. Как и задача компилятора(ide) проверять синтаксическую корректность кода. Ненужно это делать за них. Это пустая трата сил на то, что реализуется автоматикой. Всё что автоматизируется должно быть автоматизировано.

                Я про случаи когда

                Ну это просто си-api.

                Вполне себе С++, вполне себе хороший способ убрать компиляцию тяжелых частей в локализованные .cpp. Кроме прочего, таким образом можно уменьшить количество инклудов которые расползутся по проекту, ставя их только в .cpp

                Ещё раз. Если ваша библиотека ho, то вы никаким образом подобное не сделаете. Если вы сделаете шаблонное api, то его нельзя будет засунуть в cpp. Вы можете сделать только сишное api.

                Таким образом мы просто сделаете обёртку над шаблонной ho-библиотекой. Удобство сишного api крайне сомнительно в случае какой-то обобщённой логики. А такой логики в современной С++ навалом.

                Пример интересный, спасибо, но не уверен что все это так же хорошо масштабируется на относительно большие проекты

                Я думаю, что хром является «относительно большим проектом». Там всё так же работает. Именно это они и сделали.


                1. mapron
                  19.06.2019 13:37

                  «Если вы сделаете шаблонное api, то его нельзя будет засунуть в cpp»
                  Что за бред?
                  Я даже больше скажу, его еще и из dll можно экспортировать, если типы с которыми может быть инстанцирован шаблон, заранее известны) У нас в проекте это активно используется, для шаблонов, которые используются с 2-3-4 типами.
                  «есть ограничения» != «нельзя».


                  1. twinklede
                    19.06.2019 13:41
                    -2

                    Что за бред?

                    Бред это ваши рассуждения вызванные вашем же непониманием. Шаблон нельзя никуда эспортировать по определению. Эспортировать куда-то можно инстанс.

                    У нас в проекте это активно используется, для шаблонов, которые используются с 2-3-4 типами.

                    Что за бред? Во-первых 2-3-4 типа это не шаблон, у шаблона нету никаких ограничений. Во-вторых эспортируются не шаблоны, а инстансы.

                    Узнайте разницу между первым и вторым перед тем как кричать «бред».


                  1. 0xd34df00d
                    19.06.2019 15:40
                    +1

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


                    Разговор ведь не об этом.


          1. lgorSL
            19.06.2019 12:44

            современный С++ разделить вообще почти нельзя.

            Я думаю, имелись ввиду inline, constexpr и шаблонные функции/классы.


  1. twinklede
    19.06.2019 10:02

    Затем сравнил с компилятором на C++, и там вполне ожидаемо компилятор был примерно на 30% больше

    Что это за ангажированные намёки ничем не обоснованные? Это сравнение, либо пропаганда?

    Rust (основа для сравнения)

    На каком основании это основа, а не это:

    Rust (другая группа): трёхкратный размер из-за иного дизайна!


    Почему эта разница в одном случае описывается дизайном, а в другом непонятным набором слов?

    Я не очень глубоко копался в этих различиях по размеру. Предполагаю, это в основном объясняется:

    А раньше же было «ожидаемо», а теперь «не копался», «не знаю»? Зачем что-то утверждать, если при этом ничего не знать?

    Отсутствие типов sum и сопоставлений с образцом в C++, которые мы широко использовали и которые были очень полезны.

    Никакое «сопоставлений с образцом» не является чем-то необходимым и в 95% случаев сливает более мощной концепции визиторов. В С++ есть исключения и ненужно матчить каждую функцию. К тому же, с чего вдруг в С++ нету сумтипов?

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

    Необходимость дублировать все сигнатуры в заголовочных файлах, чего нет в Rust.

    То, что это попросту обман я уже писал. Необходимость что-то там дублировать обусловлена разделением на h/cpp-файлы. Никак не обусловлена С++ и это разделение может не использоваться.

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

    Какая разница какие функции? К тому же, тут явно видно взаимные противоречия.

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

    Это просто глупость несусветная. Это мифология, которую была опровергнута уже тысячи раз. Особенно хромом. Уплотнение кода способствует ускорению компиляции т.к. инклюды в С++ тяжелые и компилятор делает ненужную работу каждый раз обрабатывая один и тот же код в каждом cpp-файле.

    К тому же, уровень владения языком представителей С++-лагеря крайне скудны(судя по тексту). В С++ непросто так появилось ho и причины тому просты — шаблонный код попросту нельзя разделить. Попытки были и все они не имели успеха.

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

    8733 строк

    Никак не могут компилироваться секунды. К тому же сравнение скорости компиляции на разном железе меня так же удивляют. А так же меня удивляет отсутствие информации об компиляторе и какой-то конкретики в целом.

    Опять же. Кода мы не видим. Никаких измерений нет. Есть «одна бабка сказала» и какие-то ничем не обоснованные заявления. Прослеживается явная ничем не обоснованная ангажированность и двойные стандарты.


    1. develop7
      19.06.2019 12:36

      К тому же, с чего вдруг в С++ нету сумтипов?

      есть, есть std::variant, которым пользуется примерно никто


      1. twinklede
        19.06.2019 12:48

        есть, есть std::variant, которым пользуется примерно никто

        Это ничего не значит, да и попросту враньё. На это я могу ответить: есть rust|haskell, которыми пользуется примерно никто.

        PS Опять табун раст-адептов набежал и начал гадить в карму.


        1. Dark_Daiver
          19.06.2019 13:01

          На самом деле, здорово бы было, если вы привели бы пример сумтипов на с++ вместе с компактным примером их использования.


          1. twinklede
            19.06.2019 13:08

            На cppreference есть вполне адекватные примеры.


            1. Dark_Daiver
              19.06.2019 13:09

              Спасибо, то что нужно


            1. develop7
              19.06.2019 22:35
              +1

              Напоминаю: Haskell и Rust мозголомные и непонятные, а C++ — простой и очевидный. Смотрите не перепутайте!


        1. technic93
          19.06.2019 17:21

          Спросил у автора (т.к. это был перевод). Вот его ответ:
          "As far as I know my friend didn’t use the new visitor feature."
          Вообщем там наверно обходились enum + switch + cast/union.


        1. develop7
          19.06.2019 22:27
          +1

           Это ничего не значит, да и попросту враньё

          я ищу вот так, находятся тесты из LLVM пополам с игрушечными проектами с нулём звёзд. И в QT нашёл QVariant, который умеет оборачивать variant при наличии, который, в свою очередь, находится в игрушечных проектах пополам с копиями qvariant.h


          покажите, как правильно искать


          1. 0xd34df00d
            19.06.2019 22:43
            +1

            Не, ну я в своих проектах std::variant юзаю вместе с самописным костылем для эмуляции недопаттернматчинга через лямбдочки. Это даже удобно, в принципе. Ну, насколько код на плюсах может быть удобным.


            Вот что действительно никто не использует — какой-нибудь Mach7.


    1. 0xd34df00d
      19.06.2019 15:43
      +1

      Никакое «сопоставлений с образцом» не является чем-то необходимым и в 95% случаев сливает более мощной концепции визиторов.

      Сливает по какому критерию? Что по выразительности, что по числу строк кода оно, наоборот, уделывает.


      Как вы на визиторах замачтитесь на значение без того, чтобы городить тонны ифов?


      1. develop7
        19.06.2019 22:37

        анонимным классом с перегруженным operator() :)
        производится ли при этом проверка на exhaustiveness, я не понял


        1. 0xd34df00d
          19.06.2019 22:45
          +1

          Для, э, конструкторов в ML-смысле производится. Для значений вам придется писать ифы, не сможете вы выразить матч на значение, а не только лишь на форму конструктора.


    1. Gorthauer87
      19.06.2019 17:00
      +2

      Хех, а вот и адепты церкви свидетелей крестов подъехали.


    1. mayorovp
      21.06.2019 00:59

      То, что это попросту обман я уже писал. Необходимость что-то там дублировать обусловлена разделением на h/cpp-файлы. Никак не обусловлена С++ и это разделение может не использоваться.

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


  1. twinklede
    19.06.2019 10:08
    +1

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

    Ой, оказывается те другие попросту использовали «сопоставлять шаблон» и это было плохо. Но это ведь была проблема С++, а теперь оказывается rust-код отказывается от pm в пользу визиторов, которое в С++ попросту нативны(в отличии от rust, где это делается через тысячи костылей и боейлерплейта).

    Каждая новая строчка пробивает очередной дно.


    1. twinklede
      19.06.2019 13:30
      -4

      Гадящие в карму адепты раста. Вы так и будите из треда в тред сливаться и гадить? Где же ваш код, где же ваши ответы? Нету, не можете? Зачем гадите?


      1. vedenin1980
        19.06.2019 13:49
        +4

        Совет. Не использую ни раст, ни С++, но чем больше вы используете фразы «Каждая новая строчка пробивает очередной дно», «сливаться и гадить», «гадящие в карму», «бред это ваши рассуждения вызванные вашем же непониманием», «да и попросту враньё», тем больше вам будут снижать карму, причем даже если вы писали по делу.

        Ну вот не любят на Хабре такой тон, для многих это как красная тряпка, причем поставить минус могут даже не разбираясь кому и почему вы так писали (бывает прилетает даже когда оппонент явный тролль). Сомневаюсь, что хотя бы один минус вам прилетел именно от адептов раста.

        P.S. Я вам минусов в карму и комментарии не ставил.


        1. twinklede
          19.06.2019 14:08
          -5

          Совет. Не использую ни раст, ни С++, но чем больше вы используете фразы «Каждая новая строчка пробивает очередной дно», «сливаться и гадить», «гадящие в карму», «бред это ваши рассуждения вызванные вашем же непониманием», «да и попросту враньё», тем больше вам будут снижать карму, причем даже если вы писали по делу.


          Ваши рассуждения не работают. Гадить начали до всего, а значит не в следствии. «бредом» было названо моё утверждение, где ваш коммент там? Нету. А что же так? Неужели вы просто пытаетесь что-то оправдываться, даже не разобравшись в теме?

          Ну вот не любят на Хабре такой тон, для многих это как красная тряпка, причем поставить минус могут даже не разбираясь кому и почему вы так писали (бывает прилетает даже когда оппонент явный тролль). Сомневаюсь, что хотя бы один минус вам прилетел именно от адептов раста.

          Именно адептов. Эти адепты набегают табунами из всяких чатов и прочих сборов и минусуют. Это мною доказанный факт.

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

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

          Всё это было мною разобрано и доказано тысячи раз. Разберитесь перед тем как кого-то оправдывать.

          Ах да, существуют адекватные адепты раста. Они способны писать код, способны понимать. Но проблема в том, что их очень мало и адекватный человек просто так не гадит. Гадит только тот, кому нечего сказать. Кто пытается заткнуть меня/неугодных.

          Я вам ещё раз советую почитать историю Посмотреть как мне угрожали эти адепты. И где же все честные люди были, когда мне писали откровенные угрозы? А, никого не было.


          1. twinklede
            19.06.2019 14:38
            -3

            Адепты увидели ни на чём не обоснованный трёп и начали плюсовать того, кто нагло врёт и оправдывает их. И гадят, гадят. Ведь ничего иного они не могут.


            1. iroln
              19.06.2019 14:54
              +1

              У вас бред преследования, обратитесь к врачу.


              1. twinklede
                19.06.2019 15:08
                -2

                У вас бред преследования, обратитесь к врачу.

                Отличная история. Адепты гадят и делают вид будто бы ничего не было. Ответы будут? Ответы за угрозы, ответы за то, что гадят? Где ответы? А Нету ответов, есть только «Гадят» и нелепые обвинения уровня начальной школы


                1. iroln
                  19.06.2019 15:13
                  +2

                  Я вас ни в чём не обвиняю, просто читая ваши комментарии складывается впечатление, что вы не совсем адекватны (именно реакция на критику, минусы в "карму" и т. п.). Почему вас так волнует, что кто-то в интернете поставил вам минус в какую-то "карму", как от этого изменится ваша жизнь? Социальный рейтинг у нас ещё пока не ввели. Почему вы так агрессивно и остро реагируете? Симптомы бреда преследования прослеживаются (какая-то группа лиц желает зла, гадит и угрожает).


                  1. twinklede
                    19.06.2019 15:29
                    -4

                    (именно реакция на критику, минусы в «карму» и т. п.).

                    В чём она неадекватно? В том, что обиженные адепты раста гадят в карму и минусуют, набегая на меня табунами?

                    Я вам так же советую изучить тему перед тем как делать какие-то выводы. Это уже не первая тема в которой они гадят. В корой угрожают. В которой они проигрывают.

                    Почему вас так волнует, что кто-то в интернете поставил вам минус в какую-то «карму», как от этого изменится ваша жизнь?

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

                    Почему вы так агрессивно и остро реагируете? Симптомы бреда преследования прослеживаются (какая-то группа лиц желает зла, гадит и угрожает).

                    У меня всё задокументировано. А вы просто кидаетесь нелепыми обвинениями.


            1. vvzvlad
              19.06.2019 15:43
              +7

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

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


  1. dzolotarev
    19.06.2019 10:31

    Несодержательно. Гораздо интереснее бенчмарки и сравнение временных затрат на разработку. Например, на С++ x человеко-часов, на Rust — y, на Python — z. Ну особенно интересно сравнить в этой задаче производительность Rust и C++.


    1. Dark_Daiver
      19.06.2019 12:00
      +1

      К сожалению такой подход будет содержательным, только когда у команд одинаковый опыт, и задача решается с использованием одних и тех же алгоритмов


  1. lgorSL
    19.06.2019 10:41
    +3

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


    И я почему-то уверен, что если бы такой курс был, мало кто из наших студентов использовал бы немейнстримные языки типа Haskell/Rust/Okaml/Scala.


    Что-то похожее по уровню хардкорности было в шаде, но он больше про машинное обучение, построения компиляторов и тому подобных курсов там нет.


    • В шаде на курсе по С++ писал интерпретатор лиспа. Как мне показалось, для подобных штук плюсы не очень удобно использовать, код получается довольно громоздким. Если бы я писал код на скале, было бы в 2-3 раза короче. Меня удивляет, что в статье выше код на плюсах получился не таким уж и большим — видимо, кто-то познал дзен и очень хорошо выбрал уровень абстракций.
    • Ещё (там же в шаде) на курсе по питону была домашка с интерпретатором питоновского байт-кода, написанном на питоне. Но, видимо, мой мозг слишком привык к статической типизации, т.к. динамическими фишками питона я почти не пользуюсь и в большинстве задач код получается таким же или дальше длинее, чем в скале.

    P.S. А где вообще есть хорошие хардкорные курсы по классическому CS с построением компиляторов, алгоритмами и прочим? Интересуют как онлайн курсы, так и аспирантура в России/за границей.


    1. lahmatbiy
      19.06.2019 11:46
      +1

      СПбГУ, Булычев или Григорьев (лаба языковых инструментов JB)
      Будет Вам млн CS во всех проявлениях


    1. vedenin1980
      19.06.2019 12:39
      +1

      Можно говорить, что российское образование впереди планеты всей и хакеры тоже, но на практике у меня в институте подобного курса в программе не было


      Это Миф. Я разговаривал со своим боссом (который получал образование у нас и в штатах и создал международную ИТ компанию с филиалами в России, соотвественно мог сравнить образование у нас и Западе). Вот он говорил «В России отличное школьное образование, отвратительное высшее и хорошие программисты». Вот такой парадокс.

      Суть в том, на Западе они сами выбирают нужные им курсы, свое расписание и получают знания без воды и устаревших курсов уровня программирования на перфокартах. У нас хорошими программистами становятся не благодаря высшему образованию, а скорее вопреки. Просто программирование это чуть ли не единственная профессия в РФ для умных технических ребят, где можно получать очень неплохие деньги для РФ и уехать почти в любую страну на зарплату выше средней.

      На Западе профессия программиста не настолько привлекательна, плюс школьное образование дает относительно хорошую базу, а высшее в РФ… ИМХО, проигрывает даже простому самообразованию, может не во всех программерских вузах, но во многих.


    1. LinearLeopard
      19.06.2019 16:17

      Как уже было замечено в наших краях хорошее школьное образование, плохое высшее и отвратительные магистратуры, в США всё наоборот.

      Возвращаясь к компиляторам: есть курс от computer science center www.youtube.com/watch?v=DKr45aBUtFU&list=PLlb7e2G7aSpQ4Ym2TWTYyMcfMevxpKoxj

      Он там не один такой, есть прочитанные в другое время. Я сам не интересовался, за качество отвечать не берусь, но ознакомиться можете.


    1. nexus478
      19.06.2019 16:47
      +1

      P.S. А где вообще есть хорошие хардкорные курсы по классическому CS с построением компиляторов, алгоритмами и прочим? Интересуют как онлайн курсы, так и аспирантура в России/за границей.

      У JetBrains и Computer Science Center вроде интересная магистратура в ИТМО, вот программа, а вот темы работ недавних выпусников. Конечно, много прикладного, но какой-то чел защищался по завтипам, например.


  1. agalakhov
    19.06.2019 11:16

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


  1. dbelka
    19.06.2019 11:51
    +2

    Думал будет всестороннее сравнение: скорость разработки, скорость выполнения, читабельность итд…
    А тут просто wc -l сделали.


  1. Megadeth77
    19.06.2019 14:00
    -2

    Отлично! Чтд. Динамическая типизация рулит. Таскать с собой зверское мозголомство с ковариантными и контрвариантными шаблонами чтобы случайно переданную str вместо int отловил компилятор, при том что такая конструкция завалится на первом же тесте — ну такое.


    1. Crysdd
      19.06.2019 15:31
      +4

      Да вроде бы в том и плюс — не писать тесты там, где может справиться компилятор/интерпретатор.


      1. Megadeth77
        19.06.2019 16:28

        в статье как раз есть абзац про трудоемкость описания системы типов, при том что тесты придется прогонять и там и там.


        1. Gorthauer87
          19.06.2019 17:05

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


        1. 0xd34df00d
          19.06.2019 17:13
          +1

          Я, наверное, слишком быстро читал, но пропустил этот абзац.


          Или вы имеете в виду тот, где говорится о том, что корректность решения задания будет проверяться тестами?


          1. Megadeth77
            19.06.2019 18:19

            Одна большая часть этого различия, вероятно, динамическая типизация. Только в нашем ast.rs 500 строк определений типов, и ещё много типов, определённых в других местах компилятора. Мы также всегда ограничены самой системой типов. Например, нам нужна инфраструктура для эргономичного добавления новой информации в AST по мере прохождения и доступа к ней позже. В то время как в Python вы можете просто установить новые поля на узлах AST.


            1. 0xd34df00d
              19.06.2019 18:55
              +3

              Так именно ради этого и есть trees that grow. Притом это не «напихали чо-т в словарь, вроде работает», а полноценные статически проверяемые тайпчекером аннотации.


              Другое дело, что в Rust это нереализуемо (согласно тому, что я знаю о его системе типов).


  1. alsoijw
    19.06.2019 21:27
    +1

    Сравнение одинакового проекта в Rust, Haskell, C++, Python, Scala и OCaml
    А в каком месте он одинаковый? Алгоритмы разные, абстракции разные, готовность разная, количество дополнительных функций тоже разное. Тем более, что больше всего отличаются друг от друга реализации на одном и том же языке. Почему код меряют в байтах и строках?


    1. technic93
      20.06.2019 00:01

      Это сравнение интересно однокурсникам автора и самому автору для резюме. Без более детальных подробностей что то обсуждать сложно.


    1. red_andr
      20.06.2019 00:09
      +1

      Так скажем, задача была поставлена одинаковая.


  1. red_andr
    20.06.2019 00:20

    К слову, Waterloo самый лучший университет в Канаде в компьютерной области и пожалуй один из самых в мире. Там, к примеру, написали знаменитый Watcom и создали Maple. PHP, QNX и BlackBerry тоже оттуда же.


    1. gecube
      20.06.2019 21:45

      Не кажется ли вам, что php лишний в этом списке? Это, конечно, целая эпоха, но вспоминать про нее не хочется
      Ко всем остальным питаю теплые чувства


  1. stalkerg
    20.06.2019 13:49
    -1

    Отичный пример почему динамическая типизация это очень круто если применять правильно!
    Кода гораздо меньше и результат получен одним человеком а не двумя-тремя.


    1. Megadeth77
      20.06.2019 14:49
      -1

      Ща минусов отхватите за компанию


    1. alsoijw
      20.06.2019 20:54
      +2

      С тем же успехом можно и в C# писать как на javascript и получится ненамного многословнее. Вместо

      val['sum'] = val['a'] + val['b']

      Будет
      val["sum"] = (int)val["a"] + (int)val["b"]

      А если ещё и паттерн расширения прикрутить для хеша или объекта, то совсем динамический язык получится, поскольку можно будет не только значения брать, но и методы вызывать.

      ИМХО динамическая типизация хороша либо для скриптов которые можно написать за пять минут, либо как промежуточное представление.


    1. freecoder_xx
      20.06.2019 21:45

      О результатах мы мало что знаем, особенно о стоимости отладки и доработки.