Pull to refresh

Comments 275

Как язык C++ довольно ужасен и неоднозначен. И с точки зрения концепций, и с точки зрения синтаксиса, и с точки зрения инфраструктуры.

Новые стандарты его, к сожалению, особо лучше не делают. Нет, конечно, и хорошие вещи приходят, но часто это "OMG WTF"?

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

Вы забыли поставить запятую после "Как язык".
Что касается "ужасен и неоднозначен", Вы хотите начать "священную войну" ? :)
Но, ... Вы сами ответили на данный вопрос. Да, "единственный ОО-язык высокого уровня, на котором можно напрямую общаться с железом...".

Какие конкретно концепции С++ вам не нравятся? Время показывает, что они крайне хороши.
Претензия по синтаксису тоже непонятна
> Но это практически единственный ОО-язык высокого уровня,
С++ это не ооп язык

С++ это не ооп язык

Мультипарадигменный, как и большинство современных языков. Да, в идиоматическом C++ писать в чисто ООП стиле не принято и не особо-то эффективно, но формально C++ вполне себе ООП, равно как и процедурный, структурный, функциональный язык.

Не нравится концепция UB и, как следствие, невозможность писать код, которому можно доверять.

Так пишите код, в котором нет UB. Оно проявляется в таких конструкциях, за которые и так бы по рукам надавали.

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

Писать на плюсах код без UB очень легко! Но ровно до тех пор, пока не вляпаешься в своё первое UB на продакшене (в дебажном билде они обычно не случаются)

Когда я с 0 учил язык, он выглядел просто как очередной язык программирования, просто с более строгой структурой описания алгоритмов. Но когда в конце циклов статей осталась последняя глава "шаблоны и STL"... Я перестал мыслить как нормальный человек. Ты буквально делаешь компилятор в компиляторе, где остановит тебя только фантазия, а твой код всегда заботливо оптимизирует компилятор))

Ps Делал свойства как в С#. Делал запись типа int x = classT.at[4] валидной, где classT { void* ptr; };

А потом в "твоих" фантазиях должен сидеть и разбираться другой человек, потому, что ты заболел/уволился/повысился и т.д.
Так и рождается код о котором говорят "работает вот и не трогай", т.к. в нём никто не может разобраться и контакта с автором больше нету, а на код завязано, что ни будь важное и тестов никто не написал. Никто его ни отрефакторить не может ни подправить, в некоторых случаях даже компилятор обновлять может быть страшно, так все и живут с этим "фантастическим" куском ... кода годами и десятилетиями без возможности сделать шаг влево/вправо.

При всем уважении к C++ и моему опыту на нем - не надо писать на нем вообще ничего сейчас, кроме очень-очень редких вещей, типа виндовых кернел драйверов (никсовые на расте уже можно).

Отсутствие кучи всего из коробки, боль с депендами даже при использовании депенденс менеджера, сложность отладки, УБ, зоопарк из систем сборки, где нормальная ровно одна (нет, не CMake) и прочее - вы поняли.

Если писать только для десктопа последних поколений перекладывание джсонов и подобное - может быть и согласен, но C/C++:

  • универсален, кроссплатформ (да, #ifndef, и что? Kotlin, where are you?)

  • есть ВСЕ, не из коробки, да, но есть

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

  • работало в 2000-м - заработает и сейчас (см.: питон2, питон3)

  • не зоопарк, а ассортимент, отладка бывает сложна только в связке с каким-нибудь Fortran, но где еще так быстр __matmul?

  • дружит со всеми БД, интегрируется со всем подряд

    Но если мы пойдем чуть подальше в эмбед:

  • запускается на любом утюге

  • тулчейны на ВСЕ,

  • автоген доков (вообще найс)

  • миддлов есть много, поддержка недорога

    Чем cmake не угодил?

  • прост как три копейки без всяких JIT, easy to start, hard to master

  • вечно молод, не надо каждые лет 5 смотреть на очередной Ruby on Crooked Rails

Вместо срача - просто рекомендую попробовать современный C#. Вот уж где реально универсален, кроссплатформен, есть ВСЕ, быстрый, депенденс менеджер нормальный и встроенный. одна система сборки человеческая, работающая везде.

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

Не хватает только нормальной кроссплатформенной оконной библиотеки.

Если говорить про "лучший" C++, то это будет не C#, а Rust. Там по-человечески сделана система управления ресурсами/lifetimes - исправлено то, за что справедливо критикуют C++ в области работы с памятью. При этом отсутствуют ужасы null-values и есть явное разделение ссылочной и by-value семантики

Если бы ещё синтаксис расту разрабатывали без цели увеличения дохода офтальмологов...

template<typename Не, typename = в:: плюсах>
auto на(это) -> decltype([]() { жаловаться })

В нынешнее время с обилием тулинга не составит труда написать переводчик синтаксиса во что душе угодно и обратно.

Написав на Rust относительно немного кода, после лет в С++, не вижу проблем в его синтаксисе вообще, особенно по сравнению с С++.

Разве что то, что он пошёл не только от С, как в PHP или Javascript.

Другими словами, да, программист на С++ может выучить PHP за выходные, а Rust не может. Но это не проблема Rust, а следствие из более продвинутых фич, которых в С++ не существует: expression syntax, rich enums, borrow checker, и что там ещё.

Каждый раз, когда кто-то в очередной раз ругает синтаксис Rust, я предлагаю указать, что именно с синтаксисом не так. Где-то 90% отваливаются на этом вопросе, а те, что таки отвечают по существу, в итоге предлагают что-то значительно хуже.

Ну вот берём прям первое, что видит программист, заинтересовавшийся растом:

    println!("Hello World!");

Почему посреди фразы здесь знак "!"? Как мне прочитать это вслух? Люди привыкли к синтаксису английского языка, практически все языки программирования пытаются так или иначе к нему приблизиться.

Ок, берём задачку чуть сложнее: посчитать количество символов "_" и "-" в строке.

Код из cargo, каноничнее некуда:

impl<'s> UncanonicalizedIter<'s> {
    fn new(input: &'s str) -> Self {
        let n = input.chars().filter(|&c| c == '_' || c == '-').count() as u32;

Что означает весь этот мусор? <'s>, |&c| c - зачем всё это? Как это прочитать вслух?

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

Т.е. это вслух читается на ура? А знаки "меньше" странно сгруппированные по двое ну вот совсем никого не смущают.


std::cout << "Hello world!" << std::endl;

std::map, std::string, std::vector,std::cout, std::endl,…
Ужас. Зачем тут постоянно повторяется std::, зачем этот мусор?

Потому что руко**опы из комитета не смогли сделать нормальный using namespace. Теперь везде этот std::мусор.

Впрочем, если вы точно знаете что делаете, вы всё ещё можете написать using namespace std; , и тогда всё будет вполне понятно и красиво:

cout << "Hello, world!" << endl;

Потому что руко**опы из комитета не смогли сделать нормальный using namespace.

Да потому что невозможно похоронить тянущееся с 60-х годов прошлого века наследие в виде include-файлов и препроцессора.


Впрочем, если вы точно знаете что делаете, вы всё ещё можете написать using namespace std;

Как альтернативу, комитет предложил модули, где каждый файл — отдельный translation unit, и где вы вполне можете делать подобные вещи.

И что мешало в том девяносто лохматом году, когда вводили namespace, объявить, что теперь каждый неймспейс компилится как независимая либа и подключается как либа? Тулинг это поддерживал и тогда, просто это требовало дополнительных приседаний по настройке сборки, а программистам, как обычно, лень.

Времена, когда язык программирования выглядел как текст на английском (COBOL), уже давно прошли. Это слишком многословно и все равно вынуждает учить синтаксис язык программирования.


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

Я, например, до Си - подобного синтаксиса познал синтаксис Pascal/ADA и переход на С++ мне казался адом, до некоторого момента. Раст мне нравится, его концепция, философия и даже синтаксис местами. Но почему код на Хаскеле кажется более читаемым? И это не только мое мнение. Ведь человек, который легко читает код на Хаскеле, наверняка осилит и любой другой, но это не работает с Раст почему-то.

А вы Хаскель с первого дня легко читаете? Я вот погуглил примеры кода, как-то не очень понятно выглядит:
Пример
image

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

Ну, знаете, некоторые люди иногда общаются. Голосом:)

А ещё, некоторые человеки, наткнувшись на непонятное место в тексте, непроизвольно начинают проговаривать прочитанное вслух (хоть и про себя).

Почему посреди фразы здесь знак "!"?

То, что это макрос объясняется в первой же главе книги по языку. Ну да, с какой-то теорией придётся ознакомится, иначе, в зависимости от предыдущего опыта, можно очень до многого докопаться. Например, зачем в С++ нужно ->? Вон в С# всё через точку. Если что, не надо мне объяснять, а на С++ много писал, это просто пример.


Ок, берём задачку чуть сложнее: посчитать количество символов "_" и "-" в строке.

К "задаче" относится только часть приведённого фрагмента кода, поэтому можно упростить до input.chars().filter(|&c| c == '_' || c == '-').count(). Ну или, по крайней мере, до вот такой функции:


fn num_chars(input: &str) -> usize {
    input.chars().filter(|&c| c == '_' || c == '-').count()
}

Не всё так страшно, не правда ли? Кажется, остаётся только претензия к |&c|. Если амперсанд забыть, то компилятор точно скажет, что не так, ну а различать значения и ссылка вполне нормально для системного языка. Ну и наконец синтаксис лямбд в С++ тоже достаточно страшный/перегруженный и ничего.

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

Вы не поверите, но точно такое же ощущение у меня возникает, если взглянуть на текст на китайском, японском или корейском

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

Всё-таки не могу согласиться. Мне кажется, что такое мнение может сложится разве что у человека, который кое-как освоил один язык. Вероятно, динамический — там код обычно действительно проще выглядит. А если осмотреться по сторонам, то начинаешь гораздо спокойнее к этому относиться. Я вот сложные LINQ выражения с трудом читаю, но C# разработчики, вроде как, весьма довольны этим инструментом. В скале разрешено вводить произвольные операторы, и как-то живут же. И это я ещё не говорю про лисп или хаскель.


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

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

Что касается раста - там есть несколько шероховстостей, но чувствуется, что ребята очень щепетильно относятся к синтаксису и хорошо поработали. macro_rules!, кстати, тоже неплохи. Просто это другой язык, который редко используется.

Мне кажется, что рассказы про вырвиглазность раста, надуманные проблемы с unsafe и прочее - это просто проявление парадокса блаба. Разве что, вместо unsafe можно было бы выбрать более точное слово и &mut заменить на какой-нибудь &uniqe.

Разве что, вместо unsafe можно было бы выбрать более точное слово и &mut заменить на какой-нибудь &uniqe.

Помню споры на эту тему. Но тогда непонятно, что делать с переменными (let и let mut). Вводить отдельное ключевое слово? Есть некоторая прелесть в однообразии.

В C++ макросы писать тоже не то чтобы просто, но связано это не с их сложностью, а с примитивностью. Чтобы скормить макросу что-то шаблонное надо городить костыли, потому что нормально поделить на выражения препроцессор не может, удивительно что он хотя бы круглые скобки умеет учитывать. Variadic до C++20 в принципе практически бесполезен, ибо без __VA_OPT__ сделать с аргументами что-то можно только нагородив кучу бойлерплейта из разряда MACRO_8_ARGS, MACRO_7_ARGS и т.д. С __VA_OPT__ получаются конструкции, по сложности сопоставимые с шаблонами. А, ещё до C++20 в variadic макросах должен быть хотя бы один аргумент. macro_rules! были бы манной небесной

Попробуйте вслух и с выражением прочитать C++ версию. Не вижу особенной разницы.

UncanonicalizedIter::UncanonicalizedIter(std::string_view input) {
  uint32_t n = input 
    | views::filter([](auto c) { return c == '_' || c == '-'; })
    | ranges::size();

Не хватает ещё clang-овских аннотаций лайфтаймов.
ranges::size разве умеет работать с views::filter?

Я не пытался это собирать, просто накидал приблизительно похожий код :)

судя по "null-values" вы совершенно не знаете ни раст, ни С++.
Раст это язык, который дизайнили для отсутствия сегфолтов(спойлер - не вышло, см. unsafe который ВЕЗДЕ).
С++ дизайнили для качества получаемого кода: инкапсуляции сложности в больших системах, масштабирования.
Что уж говорить, если весь синтаксис раста заточен чтобы его легче было парсить(КОМПИЛЯТОРУ!), а не человеку(а нейминг просто -10 из 10)

Вот вы похоже Rust не знаете. Там unsafe не такой как C++ - то есть совсем. И дает кучу гарантий. А с синтаксисом. Просто он не сишный в своей основе - а парсить современные плюсы человеку вообще невозможно - одни темплейты вышибают напрочь на часы.

У меня отлично получается парсить шаблоны, никаких проблем.

А unsafe в расте, да, даёт кучу гарантий. Неявных гарантий которые вы обязаны соблюдать, иначе УБ.
Требований к unsafe коду в расте БОЛЬШЕ чем к коду С++ и требования не такие очевидные, как не обращаться к невыделенной памяти. Например создание ещё одной ссылки на то на что уже есть ссылка это уже уб

P.S. то что вам сложно парсить в С++ просто невыразимо в расте, т.к. там нет многих возможностей шаблонов и совсем нет вариадиков.
Смешно, в языке где в синтаксисе есть туплы количество типов в тупле ограничено!
P.P.S. если вы попробуете распарсить растовый макрос, то окажется что он значительно сложнее любого шаблона. Имеет синтаксис который вовсе зависит от левой пятки программиста его писавшего.(а макросы там буквально ВЕЗДЕ, например нет способа создать вектор без макроса)

например нет способа создать вектор без макроса

Создал: Vec::new(). Сейчас будет уточнение, что надо создать вектор с элементами?.. Ну ладно: Vec::from([1, 2, 3]).

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

Вау, только вот это пожалуй самые непопулярные конструкторы вектора.

Утверждалось, что вообще без макросов создать нельзя, оказалось, что можно. Теперь давай про десять макросов уточним. Так-то он всего один.

Если память не изменяет, то можно так Vec::new_in

К слову, о читаемости раст макросов:

Вообще язык декларативных макросов в Rust очень простой. Но от символов в глазах рябит, это правда. Сделано как проще, чтобы меньше было пересечений в обозначениях макрокоманд с остальным кодом на Rust, так как они смешиваются в макросе.

Да в чем проблема символов? Тут все так пишут словно это уже устоявшийся факт что много разных символов это плохо. Я наоборот терпеть не могу языки типа lua потому что их сложнее парсить визуально большими кусками, так как все сливается в одно оргомное сочинение. И тот же знак '!', для макросов был бы отличной штукой в C++ по которому даже без подсветки было бы легко понять что это за конструкция.

У меня отлично получается парсить шаблоны макросы, никаких проблем. (спасибо за аргумент)

Даже со старта с этим не было проблем. Если быть немного знакомым с синтаксисом старых скриптовых языков, шелла, например, где переменные и выражения берутся в ${} и $(), то вообще читается без запинки.

у шаблонов такой же синтаксис как у обычного кода, у макросов - нет

читается без запинки.

в этом чтении нет никакой информации о том какой тип ожидается, тогда как в аналогичном конструкторе std::vector из С++ чётко написано, что это size_type или что-то такое

у шаблонов такой же синтаксис как у обычного кода, у макросов - нет

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

в этом чтении нет никакой информации о том какой тип ожидается, тогда как в аналогичном конструкторе std::vector из С++ чётко написано, что это size_type или что-то такое

Ну, я бы не сказал, что вот это: https://doc.rust-lang.org/src/alloc/macros.rs.html#63 намного хуже, чем вот это: https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/bits/stl_vector.h#L84. Да, я помню, кастомные аллокаторы.

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

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

Препроцессор С ничего общего с синтаксическими макросами раста не имеет. И присутствует он там т.к. это библиотека сразу на все версии С++ начиная с незапамятных времён(а нейминг переменных специально таков, чтобы не пересекаться с именами из кода пользователей, что кстати уже не нужно начиная с С++20 модулей)
В остальном там обыкновенный код с таким же синтаксисом как в любой другой части С++.

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

Хотя, честности ради: процедурные макросы я ещё не осилил, и да, они гораздо сложнее, и завязаны на API AST. Впрочем, я слышал, что старые лисперы ими крайне довольны.

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

судя по "null-values" вы совершенно не знаете ни раст, ни С++.

Ярлыки навешивать вы умеете. А по существу скажете какие есть ужасы null-values в rust/С++? Система типов там достаточно мощная, чтобы явно выразить, что значение может или не может отсутствовать

Оу май. Это какой с#? Который тащит .NET весом 4,5Г в инсталлере? UWP или WPF? Mono? Я там был, так они в 5 или 6 емнип указатели вернули как желаемые, хотя вроде весь шарп делался чтоб от этого сбежать, лул

Только на десктоп как UI вида 90-х, проблемы с чертовым nuget (из чего там наггетсы - да из того что в маке, ага)

JIT, ГАДСТВО, С%*?:, JIT!

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

Where is my c# openBLAS?

Сразу про читаемость и переносимость и поддержку комментатору внизу напинаю:

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

@Einherjar - что в питоне делает оператор @? Если на крестах не заморачиваться чрезмерной оптимизацией через интринсики (что делают только гики, если дело не касается векторных инструкций, но и там есть либы) - читается влет, STL во все поля. Да я даже буст не юзаю - нет надобности. Читается хорошо если дробить на мелкие хорошо названные методы и не оверинжинирить. Но тут конечно да - не для новисов, можно ногу того этого.

Добавлю: взгляни на JS+REDUX+TS - вот там очень читаемо, война и мир

Который тащит .NET весом 4,5Г в инсталлере

JIT, ГАДСТВО, С%*?:, JIT!

Вылезайте из криокамеры, для C# есть AOT-компилятор, и установленного рантайма не требуется.

Только на десктоп как UI вида 90-х

Вас кто-то насильно заставляет писать на WinForms, а не на чем-то более современном?

 так они в 5 или 6 емнип указатели вернули как желаемые

щито?

Там сейчас для новых фреймворков UI, от микрософта, прикрутили фичи в виде прекомпиленных точек входа, от чего лаг при входе в приложение должен пропадать. Вот настолько там в курсе таких проблем.

(За JIT/AOT)

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

Он уже давно opensource. Вендор лок там разве что в том, что лучшая среда разработки проприетарная и под винду. Разница в скорости же там не такая существенная по сравнению с разницей в скорости разработки, удобстве инструментов и надёжности кода.

Это только для прикладных программистов есть выбор между C/C++ и C#

Для системных программистов, разработчиков встраиваемых систем собственно и выбора никакого нет.

С# никогда не сравнится по производительности с нативным компилятором.
https://habr.com/ru/post/266163/
Да и по переносимости/тиражируемости кода тоже.

Ссылке 7 лет. Вы бы еще .net 1.0 откопали.

Конечно задачи где разница в производительности очевидна найти можно, но таких довольно мало.

По переносимости как раз ровно наоборот, c++ требуется компилировать под каждую платформу с кучей #ifdef, имея на выходе 100500 разных бинарников зачастую с зависимостями от разных библиотек. Причем с кросс-компиляцией все очень плохо - собрать на винде приложение например под мак в большинстве случаев ни разу не получится и наоборот. Тогда как должным образом написанное .net приложение просто запускается на любой операционке как есть.

Как можно сравнивать C++ и C#? Совершенно разные задачи решают. С# забудут, C/C++ останется, потому что всё также надо будет писать компиляторы, игровые движки, игрушки консолей - с выдавливанием всех возможностей железа, драйвера, прошивки контроллеров и встраиваемых устройств, встраиваемые модулии в критические места приложений на других, более высокоуровневых языках (тот же C#). Например в тот же фронтенд: https://developer.mozilla.org/ru/docs/WebAssembly/C_to_wasm
Можете возразить, что игры и на C# пишут, на Unity, к которому куча модулей на C#. Ок, но runtime Unity опять же C++.

Можете возразить, что игры и на C# пишут, на Unity, к которому куча модулей на C#. Ок, но runtime Unity опять же C++.

Рантайм C# тоже на C/C++ написан. Не вижу здесь никаких проблем.


Совершенно разные задачи решают.

Вот именно. Просто надо понимать, что нет языка, который был бы хорош абсолютно везде. Поэтому хорошим решением в больших проектах будет не жрать кактус и не писать всё на C++, равно как и не писать всё на C#, а использовать сразу несколько языков.

C/C++ останется, потому что всё также надо будет писать компиляторы

«C/C++» для написания компиляторов — возможно, один из худших выборов из возможных.

и чего же такого ужасного в llvm? даже ghc через него может работать

llvm создавалась в первую очередь как часть компилятора C++, поэтому то, что она написана на плюсах, не удивляет.


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

А компилятор С++ должен быть написан на C++, потому что компилятор языка Х всегда написан на Х (сарказм)

Сможете назвать что в LLVM сделано не правильно потому что С++ не такой как надо ?

Сможете назвать что в LLVM сделано не правильно потому что С++ не такой как надо ?

Эмуляция сумм-типов через наследование.

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

Но у меня вопрос где вы это нашли? Я знаю там только https://llvm.org/doxygen/PointerSumType_8h_source.html
но он не реализован через наследование.

Там разные типы узлов в AST сделаны именно через наследование.

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

Это именно что ограничение языка. В clang используют наследование для типов узлов в AST. В GCC используют… Ну, не наследование, но его эмуляцию, указатель на структуры разных типов с общим префиксом, которые в рантайме различаются по уникальным номерам.


std::variant появился только в C++17, и пользоваться им неудобно.

Во-первых, «сделано неправильно» — это не совсем тот тезис, который я высказывал. Что будет сделано неправильно в сайте, если я напишу бекенд не на питоне или на чём там его пишут, а на C++?


Во-вторых, там много что сделано неудобно. Типы-суммы через наследование, как рядом пишут. Наркоманские визиторы и полуручная беготня по дереву вместо нормального uniplate или, прости ЛММ, линз. Неочевидный API, которым очень сложно пользоваться правильно (особенно не в сишной libclang, а в более богатом плюсовом API — например, kdevelop при его использовании падает постоянно внутри clang'а). И это я говорю как пользователь llvm/clang, а не как автор.

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

Я не работаю на С++.
Меня раздирает профессиональное любопытство по части одного из моих программных продуктов, который был построен в среде, ядро которой почти на 95% в свое время было построено на С++.
Вопрос состоит в том, сколько времени ушло бы у кодера С++ на написание аналогичного продукта (настраиваемый фреймворк в абстракции графического интерфейса и инструкциями для автономного программирования и управления FSM под OS Win 10) что по сути является симулятором контроллера с расширенными функциями для работы с I/O (USB - 160/1600).

работало в 2000-м — заработает и сейчас

…если было написано в соответствии со стандартом. А это, скорее всего, не так.

Не, ну если найти компилятор из 2000 года - то заработает.

Впрочем, с остальными языками та же фигня.

Мало найти компилятор. Надо ещё и древние зависимости откопать, ведь компилятор 2000 года не сможет скопилировать современный код. А потом окажется, что древние зависимости работают только со старыми либами, которые уже не работают в современной операционной системе. Или или сам компилятор запустить не получится.

Вы ещё не забыли, что ищете компилятор по причине того, что выдуманный код у вас выдуманно поменял поведение?

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

всё будет работать, даже если там было дикое уб достаточно будет компилировать без оптимизаций

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

дружит со всеми БД

простите, взоржал :) Возможность слинковаться с сишной либой (и потом работать в стиле С, как хочешь так и приседай) не есть "дружит". Все остальные языки, вы не поверите, тоже под капотом линкуются с сишными либами всех БД, только они ещё мапят SQL-типы данных в свои нативные.

std::variant нужно было завозить в 1990, а не в 2020

Поинт тэйкен конечно. Может это и не "дружит", но всегда на том же компиляторе можно залезть внутрь и поправить, когда postgre в очередной раз поменяет oid. В случае с другими лезть что-то править или не представляется возможным, или начинаются войны "пакет манагер, плиз не трожь это", либо Soon(tm). Линковаться с сишной либой не из-под с/с++ - мне лично не очень приятно. Нативные типы не такая уж проблема, хуже когда на каждый чих нужно по пакету, которые еще и могут превратиться в тыкву в обновке следующего месяца.

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

Почему-то у меня обратное впечатление. Писать на плюсах - верный путь умереть от голода.

Если говорить про рынок труда в РФ, то проблема в том, что на C++ примерно 70% вакансий - это полукустарный embedded, заводы и всякие госухи с их legacy, и там с деньгами и условиями действительно нередко полное дно, если их избегать за километр, то остальные предложения по деньгам будут весьма неплохие, не особо меньше чем на других языках.

по большей части плюсану)

но вот

есть ВСЕ, не из коробки, да, но есть

Это "все" часто вырвиглазно, нередко с сишным интерфейсом, в абсолютно разных coding-style'ах, без нормального репозитория, часто хочется плеваться.

универсален, кроссплатформ

Только пересобирать каждый раз надо под каждую платформу. А если платформ >2, то уже проблемы.

Дополню:

Геморой с зависимостями. Авторы либы X сломали бинарную совместимость, а рассказать об этом забыли? welcome to hell. Java аккуратненько вальнется с исключением, по которому тут же, сразу же все будет понятно, в случае плюсов получите segfault хз пойми где. Надежно выручает только докер.

Но все же, в плюсах мне всегда нравился STL, он зачастую удобнее джавовых и скаловых коллекций. Задачи на том же литкоде до сих пор на плюсах решаю. Boost, я тут недавно глянул, за 6 лет, что я на плюсах не пишу, распух раза в полтора.

Проблемы начинаются когда мы пытаемся на с++ работать с вебом. Где websockets, socket.io, где нормальная работа с базами, удобная работа с json ? Все это делается через ужасные либы, и жутко не удобно.

В остальным С++ хорош конечно, но только если вы держите его далеко от веба.

Где websockets ... через ужасные либы, и жутко не удобно.

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

да, #ifndef, и что

И компилятор даже не проверяет синтаксическую корректность кода под платформы, отличные от текущей.


есть ВСЕ, не из коробки, да, но есть

Что всё? Хочу нормальную систему типов с завтипами. Ну ладно, хотя бы с Хиндли-Милнером.


Ладно, давайте поговорим о библиотеках. Хочу парсеры на монадических комбинаторах (буст.спирит, наверное, тут ближе всего, но нельзя так жить в 2022-м, да и даже в 2012-м уже нельзя было). Хочу примитивы для dataflow analysis. Хочу монадическое вероятностное программирование. Где оно всё, желательно с той же степенью выразительности и удобством пользования?


управление памятью

Сразу после исправления всех UB.


быстродействие

Есть далеко не только у него.


опыт и понимание механизмов

Опыт в чём и понимание механизмов чего?


Опыт программиста на C++ — это опыт перепрыгивания через грабли и костыли и извлечения пуль из ног. 90% этого опыта не переносится на другие языки вообще никак. Плюсы были моим основным языком лет 15, с 12 лет, как я впервые за них взялся, и лет 10 они были моим единственным языком. Весь этот опыт по борьбе с компилятором, по штудированию стандарта на предмет UB, по дрочке вприсядку с темплейтами — это всё можно выкинуть при переходе на другие языки.


работало в 2000-м — заработает и сейчас (см.: питон2, питон3)

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


Более того, в тырпрайзе на практике даже gcc 4.7 обновить на gcc 5.1 (или что там было в рхеловском DTS-2 и DTS-4 соответственно) — уже проблема на несколько лет работы выделенной команды специально обученных людей, некоторые из которых при этом заседают в Комитете.


не зоопарк, а ассортимент, отладка бывает сложна только в связке с каким-нибудь Fortran

Охохо. Отладка бывает сложна, когда перед вами приложение, начатое в тех самых 2000-х, где в одном месте возникают циклы из шаред_птров и не удаляются, а в другом месте возникают висячие ссылки, в итоге эта хрень через несколько суток работы начинает подтекать, а потом валится. Воспроизвести баг — несколько суток работы под продовой нагрузкой. Запустить под санитайзерами, валгриндом, етц — не хватает хипа и производительности. Успехов.


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

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

C++ прост, как 3 копейки? Это была шутка? Почитайте С++ language reference, это самое толстое описание языка, какое я знаю. И конечно, убивают многочисленные undefined behavior.

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

Очень, очень долгая компиляция и линковка, даже с precompiled headers, которые сам же Microsoft иногда просит отключать в сложных случаях (не надо вспоминать про incremental, кроме сборки на личном компе она мало где используется).

Кроссплатформенность отсутствует из коробки - базовые численные типы зависят от платформы.

Но и никто не спорит, что во многих нишах это по-прежнему почти единственный приемлемый выбор языка. ;)

Согласно Вашему мнению, кроме "виндовых кернел драйверов ", в нашей жизни, ничего не осталось :)

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

Ну ок, мб эмбед, я от него далеко.

Когда-то была реклама жвачки и в ней была фраза "не айс". Она стала мемом.
Так вот, не обижайтесь, но Ваш пост звучит как "не айс" в отношении к данному языку. Т.е., по вашему, он умер, по этому некроманты (а их по другому не назвать) пытаются, что бы о них не забыли, создавать новые стандарты. Ну, ок, чё.

Что касается "Все остальное лучше писать на других языках" - мой пример (не показатель, но уверен, что я не одинок), так вот даже сейчас я использую, в том числе, и MFC о котором уже наверное никто не знает (саркакзм для серьезных ;)).

P.S. вы троллите? (глянул Ваш профиль)

> вы троллите? (глянул Ваш профиль)
Нет. Я правда так считаю. Медовый месяц с языком спустя несколько лет перетек во всё большее раздражение от его использования.

А потом я познакомился с шарпом, и увидел, как можно писать код без запоминания кучи правил, без страданий с кросскомпиляцией, без борьбы с симейком, где из коробки (в нугете) есть вообще все, без мейнтенанса своего миррора с зависимостями, без кучи бойлерплейта (а-ля енум-ту-стринг или парсинг джсонов)

Заканчивая такими приятными мелочами, как компиляция за секунду для быстрых итераций разработки (и нативная компиляция для прода на CI в .net 7), сплит строки из коробки и прочие очень приятные мелочи, которые снова позволяют получать удовольствие от программирования.

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

А вот я с шарпом и не подружился, хотя писал на нем лет 5. Было много споров с коллегами по поводу разницы в понимании базовых парадигм языка относительно С (++), но пишу, нет, не нравится. Это всегда так :)

Минорный смешной референс про "троллинг" и мой профиль - я с Шандором (автором статьи) знаком, помогал ему на последнем C++ Russia в онлайне.

По сути, у C++ есть реально только два основных плюса: производительность скомпилированного приложения и непосредственный доступ к железу.

Мы используем С++ ровно из-за этих причин. Наши приложения должны работать в RealTime и постоянно общаться с внешними устройствами.

Системы сборки и зависимости библиотек и инклудов - это ад. Проблема с зависимостями более-менее решена в Linux, но почти никак в Windows. Из-за этого приходится в свои танцы с бубном.

Хорошо, что есть Qt на свете - по крайней мере, многие проблемы переносимости она решает (но не все).

По сути, у C++ есть реально только два основных плюса

C++ и C++

Не решена проблема с зависимостями в Linux. Точнее, она переложена на пакетный менеджер самой операционной системы. Какие-то популярные пакеты так поставить можно, но не более того.

где нормальная ровно одна

Посоветуете?

где нормальная ровно одна (нет, не CMake)

это вы про кого?

Но работа с изображениями — далеко не единственная область, в которой доминирует C++. С большой долей вероятности браузер, который вы используете для чтения этой статьи, также был написан на C++, как, например, Chrome и Firefox.

А разве Firefox не был переписан на Rust?

Нет, servo так и не взлетел и остался экспериментальным движком, а затем растаманы были уволены из умирающей Mozilla в большинстве своём. Сейчас судя по истории коммитов servo пилится парой человек с большими перерывами - месяцами там вообще не бывает коммитов за исключением автокоммитов от dependabot'а.

Из Servo вроде как повыдёргивали компоненты, которые сейчас используются (Quantum)

Ну да, действительно, что-то интегрировали, папочка servo появилась, но по сравнению с активностью в репозитории gecko-dev servo выглядит мертвецом. Потыкал в несколько коммитов наугад в gecko-dev - либо C++, либо js, либо служебные скрипты на питоне, активной разработки на расте не видно.

Мое личное мнение: Servo утонул под тяжестью растового легаси. Он активно писался ещё во времена не очень стабильного Rust, когда и экосистема была не развита, и практик не было выработано. Собственно, благодаря Servo (в том числе) это со временем устаканилось. Servo выполнил свою функцию. Даже удивительно, что кое-что попало из него в Firefox. В настоящей момент в Servo невозможно контрибьютить - нужно переписывать заново.

RIIR: "не результат - но процесс, не цель - но мечта".

Только маленькие части где это действительно нужно было.

Приведенные выше цифры впечатляют.

Да я аж офигел, когда увидел, что TypeScript в 5 раз медленнее интерпретирует (?) тот же самый JS код на той же самой VM. Это ж как нагло надо врать статистикой, чтобы миллиону разработчиков говорить такое в лицо?

Да, цифры этой статистики крайне тупые(сделаны фанатами раста, судя по цифрам).
Сделать чтобы С++ был в 1.6 раза медленнее С, при том что в нём компилируется ТОТ ЖЕ код это нереально тупо конечно

Ну почему же, С++ это еще и CRT.

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

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

1) CRT - это C runtime library, оно как раз для C.

2) код, написанный на C, обычно без особого геморроя можно скомпилировать C++ компилятором. Вы утверждаете, что код, скомпиленный компилятором C, выполняется быстрее, чем тот же код, скомпиленный компилятором C++?

3) В windows ядро в основном написано на C++. Если говорить про *nix, то системные библиотеки пишут на C потому, что а) у него более стабильный ABI и б) их начали писать 40 лет назад - традиция-с.

Если говорить про *nix, то системные библиотеки пишут на C потому, что а) у него более стабильный ABI и б) их начали писать 40 лет назад — традиция-с.

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

Вы утверждаете, что код, скомпиленный компилятором C, выполняется быстрее, чем тот же код, скомпиленный компилятором C++?

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

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

Там хватает неочевидных моментов. Тот же std::vector<int> например всегда будет zero-initialized если ему сделать .resize() что не очень осмысленно если он потом заполняется данными. Особенно это доставляет когда std::vector<char> используется просто как низкоуровневый буфер для сериализуемых данных, поскольку он тогда не просто инициализируется нулями, но еще и делает это побайтово (я смотрел ассемблер, выясняя чего оно тормозит, да). Это можно исправить, но это скажем так не очень очевидно и просто делается.

Таблицы свойств в С++? Хм...

Тривиальные конструкторы и деструкторы в плюсах отлично инлайнятся. С другой стороны, нетривиальные конструкторы/деструкторы есть и в С, просто они называются blabla-open или *-init, и вместо new/delete вам надо залезть в документацию и выяснить название соответствующего метода.

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

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

Открою страшную тайну, но ядро линукса написано на Си, но с использованием олдскульного ООП

Это как это чистый Си поддерживает ООП??? Это получается чистый Си это ООП язык?

Как же вас тут ООП специалисты не забодали еще :) ?

Чистый Си не мешает писать в ООП-стиле, хотя и не помогает. По крайней мере в нём есть всё необходимое - структуры и указатели на функции - для реализации примитивов ООП вручную.

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

Если брать более идиоматический код и добавить виртуальные функции, то они будут либо чуть-чуть медленнее, чем применяемые в таких ситуациях C ссылки на функции (если ссылки лежат прямо в структуре - но тогда C-шный вариант будет более прожорлив по памяти), либо такими же по скорости (если C-шный вариант применяет эрзац таблицы виртуальных методов по типу struct ..._ops), либо даже чуть быстрее (32-битный Windows ABI - thiscall более оптимально использует регистры, чем cdecl)

Если добавить исключения как идиоматическую замену кодам ошибок, то код становится еще компактнее и быстрее в "обычном" случае безошибочной работы (т.к. большинство ABI обеспечивают бесплатный try/except, а в C требуется проверка кода ошибки везде). Исключение - тот же 32-битный Windows ABI, где try не бесплатен.

Зависит от того, как писать. Если писать в стиле "Си с классами", то выхлоп будет примерно одинаковый, если активно использовать exceptions, RTTI, std::shared_ptr, std::string (которые, как известно, в наше время без COW), и подобное, то итоговый код может получиться сильно жирнее.

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

Во вторых, там простейший код с каким то алгоритмом чего-то там считать.

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

Зачем вам в строке COW вообще непонятно

то очевидно в С коде использовалось что-то чтобы их заменить.

Например, в качестве аналога тех же исключений в Си-коде зачастую используют тупо goto на нужную метку и присваивание соответствующего errno/errstr. По сути дела - одна JMP и одна MOV-инструкция. Код, который генерируется C++ для обработки исключений даже с -O3 гораздо больше и сложнее, более того, там происходит аллокация памяти, можно на godbolt проверить и убедиться.

Зачем вам в строке COW вообще непонятно

До GCC 5 у std::string вполне себе был CoW, потом его пришлось выкинуть (ибо это было нарушение стандарта, т.к. банальный [] инвалидировал итераторы), и многим взгрустнулось. Зависит от конкретной задачи и конкретного алгоритма. Например, вот тут разработчики одного большого и сложного продукта самостоятельно впиливали CoW в строки, ибо с ним им было сильно лучше.

одна JMP и одна MOV-инструкция

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

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

Если у вас обработчик вот здесь рядом, и к нему можно перейти через goto / if-ы, то вам и исключения не нужны. Собственно, в плюсах по этой причине и нет finally, чтобы не злоупотребляли исключениями в рамках одной функции.

Вы напишите goto с errno в какой-нибудь асинхронной функции, потом сравнивайте.

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

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

В плюсах есть finally

Нет. Это утверждение из серии про "дружит со всеми БД" выше.

В плюсах есть костыль для эмуляции finally. Иногда задача легко и красиво ложится на деструктор, особенно если вы заранее упоролись по RAII (это так же требует, чтобы подкапотный код, какие-нибудь сишные либы или интерфейсы, хорошо натягивались на идею RAII). Замечу, я не против RAII самого по себе, просто не всегда это оптимальное решение. А других по факту нет.

Бывают случаи, когда у ресурса больше двух состояний (он может быть не только открыт/закрыт), и вы не уничтожаете объект при ошибке, а переводите его в некое другое состояние. Бывает, что объект нельзя закрывать повторно - например, double free это UB. Наконец, чаще всего бывает, что у нас просто есть протокол, который требует после операции А обязательно сделать операцию В, не зависимо от успеха А, а А швыряется исключениями.

Во всех перечисленных случаях нам нужно либо копипастить, либо создавать аж целый класс ради вызова операции В в его деструкторе, либо колхозить флаги и if-ы c goto. Сравните с finally или, особенно, с Gо-шным defer. Язык должен помогать, а не заставлять учить паттерны ради паттернов.

Во всех перечисленных случаях нам нужно либо копипастить, либо создавать аж целый класс ради вызова операции В в его деструкторе, либо колхозить флаги и if-ы c goto. Сравните с finally или, особенно, с Gо-шным defer

Если вы после C++11 не можете сделать аналог Go-шного defer-а сами, то его можно взять, например, в GSL.

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

Чтобы язык помогал, его нужно освоить. Хотя бы.

Если вы после C++11 не можете сделать аналог Go-шного defer-а сами, то его можно взять, например, в GSL.

Ну так а я о чём? 27 строк бойлерплейта, причём ну не джуниорского уровня (эксплисит конструкторы, удаление операторов по умолчанию, не самая простая концепция std::decay_t ). Для операции, которую в других языках посчитали достаточно распространённой, чтобы ввести в язык отдельное ключевое слово.

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

Ну так а я о чём?

Я хз. Есть ощущение, что о том, в чем не разбираетесь.

27 строк бойлерплейта

И что? Этот бойлерплейт пишется один раз в какой-нибудь пространстве имен utils и используется по мере надобности. Причем не так часто, как вам может показаться.

В C++ это обычное дело. Точно так же было с хэш-таблицами до C++11 (да и после, т.к. std::unordered_map не всех устраивает), со string_view до C++17, с std::expected до C++23 и т.д., и т.п.

причём ну не джуниорского уровня

Проекты не пишутся джуниорами. Ну это я так, если вы вдруг не знали.
Причем вне зависимости от языка программирования. А уж на C++ тем более.

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

В других языках посчитали нужным и сборщик мусора в язык затащить. Давайте C++у в вину еще и отсутствие GC поставим.

В любой программе у вас будет минимум два вида строк, минимум два вида массивов

В современных условиях такое возможно только если:

a) вы имеете дело с легаси.

b) вам реально нужны эти разные типы строк и массивов. Скажем, исходя из требований по производительности/предсказуемости.

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

Вы эти сказки таким же знатокам C++ как вы расскажите. Тогда найдете благодарную аудиторию.

На практике же приходится гуглить не какие-то особенности C++ или реализацию каких-то утилитарных вещей, а детали работы условного FFMPEG и SDL. Или пояснения к спецификациям каких-то протоколов. Или еще что-то в таком же роде, что гораздо ближе к проблемам бизнес-логики, чем отсутствие finally в языке.

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

Этот бойлерплейт пишется один раз

Он (конкретно finally/defer) должен быть если не ключевым словом, то хотя бы в стандартной библиотеке. Так же, как какой-нибудь парсинг json и функциональные литералы.

В C++ это обычное дело.

Вот именно. В этой части проекта у нас QT, в этой eastl, здесь кто-то скопипастил неизвестно что со stackoverflow, здесь мы сами наколхозили, а теперь мы попробуем найти ещё одного неудачника в нашу команду.

Проекты не пишутся джуниорами.

Ага, а миддлы с сеньёрами размножаются клонированием. (spoiler: увы, нет)

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

Давайте C++у в вину еще и отсутствие GC поставим.

А давайте! Сколько там уже было попыток сделать хотя бы тупейший подсчёт ссылок в стандартной библиотеке? Не считая испытательных полигонов уровня mfc, qt, boost, mc++ и так далее? И чсх до сих пор нормально не осилили.

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

вы имеете дело с легаси ...

Мне трудно себе представить новый (без легаси) проект на плюсах, который бы не взаимодействовал с ОС, системными либами, не использовал бы интринсики и т.д. А там везде массивы в стиле С, строки в стиле С, обработка ошибок в стиле С (вместо исключений) и так далее.

Вы эти сказки таким же знатокам C++ как вы расскажите

Ну, раз дошло до личных советов, посоветую вам высунуть голову из ... норы и поинтересоваться, что там в индустрии за последние 20 лет случилось. А там, блин, долбаный js развивается в 10 раз быстрее плюсов. А там плюсы — последний популярный язык, который не умеет нормально в utf8 "из коробки". (Да-да, сейчас мне расскажут, что умеет - на том же уровне, что и finally и дружит со всеми БД)

Не то чтобы я очень хотел участвовать в дискуссии с

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

Он (конкретно finally/defer) должен быть если не ключевым словом, то хотя бы в стандартной библиотеке.

Не должен. finally/defer нужны языкам без детерминированого времени жизни объектов, т.к. там без finally/defer сложно детерминировано управлять ресурсами. Да и то, в Java к finally в итоге добавили try-with-resources, а в C# -- using. Так что сам по себе finally не так уж хорош.

В C++ с детерминированным управлением ресурсами все хорошо с самого начала, так что надобность в finally всегда была гораздо ниже, чем в языках с GC. В тех редких случаях, когда finally таки был нужен, можно было пользоваться паллиативами (BOOST_SCOPE_EXIT, gsl::finally и т.д.) Причем язык позволял создавать эти паллиативы на коленке. Попробуйте на коленке сделать defer для Go.

Впрочем, в C++ный стандарт сейчас так активно пихают всякое разное, что я не удивлюсь появлению finally в стандартной библиотеке где-нибудь в C++26 или C++29.

Так же, как какой-нибудь парсинг json

Вот этого не нужно. Во-первых, кроме JSON есть XML, YAML, TOML. И еще неизвестно что появится позже. Кстати говоря, лет 15 назад про JSON мало кто знал, а вот сожаления об отсутствии XML-я в stdlib высказывались. Сейчас вот XML уже не вспоминают особо, а будь он в stdlib, кому-то все бы это пришлось поддерживать.

Во-вторых, я не верю в то, что для того же JSON-а сделают библиотеку, которая одновременно будет и быстрой, и удобной в использовании. Включат в stdlib RapidJSON, будут ругать за неудобство, включат в stdlib nlohman::json, будут ругать за тормознутость.

Такие вещи лучше под условия конкретного проекта выбирать.

функциональные литералы.

Что это такое?

Вот именно.

Вот именно что? Если для GUI вы взяли Qt а для около реалтаймовой части EASTL, то в чем собственно проблема?

Ага, а миддлы с сеньёрами размножаются клонированием. (spoiler: увы, нет)

И какой вывод из того, что они не размножаются клонированием? Дать порулить C++ным проектом джуниору?

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

А зачем вам обратный процесс? Неужели из-за того, что на Go сложно будет решать задачи, которые решаются на C++?

А давайте!

Предлагать добавить GC в C++ и жалеть об отсутствии GC в С++ в конце 2022-го года можно только от небольшого ума, уж простите. Время уже давно все расставило по своим местам: C++ с GC не нужен. Благо альтернатив с GC, хоть managed, хоть native, в достатке.

И чсх до сих пор нормально не осилили.

И не осилят. ЕМНИП, последние кусочки из стандарта, которые хоть как-то предполагали наличие опционального GC, были в конце-концов выброшены.

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

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

Мне трудно себе представить новый (без легаси) проект на плюсах, который бы не взаимодействовал с ОС, системными либами, не использовал бы интринсики и т.д. А там везде массивы в стиле С, строки в стиле С, обработка ошибок в стиле С (вместо исключений) и так далее.

Вы не поверите, но массивы в стиле С и строки в стиле C прекрасно закрываются std::vector и std::basic_string. И обработка ошибок несложно трансформируется либо в использование исключение, либо в применение чего-то вроде std::expected.

Ну, раз дошло до личных советов, посоветую вам высунуть голову из ... норы и поинтересоваться, что там в индустрии за последние 20 лет случилось.

Да я как бы наслышан. Вот только C++, местами, все еще актуален.

А там, блин, долбаный js развивается в 10 раз быстрее плюсов.

C++ всегда развивался быстрее, чем поспевали компиляторостроители. За редким исключением в районе C++14/C++17 стандартов, когда реализация подъезжала практически одновременно с выходом фиальной версии стандарта.

А так-то уже C++20 два года назад принят, но только один компилятор сейчас его более-менее поддерживает. С принятия C++17 уже почти пять лет прошло, а в GCC/clang еще не все хорошо с поддержкой stdlib из C++17 (например, std::to_chars/std::from_chars).

И это еще не говоря о том, что в мире C++ многие еще даже на C++17 перейти не могут в силу разных обстоятельств.

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

Ну и да, еще на счет "за последние 20 лет случилось".

Это ведь еще сильно зависит от предметной области. Если вам в 2022-ом потребуется сделать форк Greenplum или MariaDB, заточенный под специфические нужды, или нужно будет начать делать кроссплатформенный видеоредактор, то скорость обрастания фичами JS-а или нововведения в Go вам вряд ли помогут.

либо создавать аж целый класс ради вызова операции В в его деструкторе

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

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

А вот исключение из finally — штука совершенно нормальная, пусть и редкая. Проблемы начинаются когда finally реализуют через деструкторы...

А вот исключение из finally — штука совершенно нормальная

Нормальная в том смысле, что не приводит к прерыванию работы приложения, так надо понимать?

Просто код finally обычные разработчики пишут так, как будто в самом finally исключения не выбрасываются поэтому можно делать действия A, B и C не заботясь о том, что каждое из них может бросить исключение.

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

  2. goto не задействует RAII и сильно сказывается на возможности оптимизации кода, который выпрыгнул хз куда

  3. Ну и да, goto так не работает, вы либо имели в виду setjmp, либо непонятно что

  4. std::string ЛУЧШЕ чем cow строка, именно поэтому отказались от cow и сделали то что сейчас. cow это вообще крайне стрёмная херня, пользы от неё примерно 0

Ну и да, goto так не работает, вы либо имели в виду setjmp

Простой goto в сишных проектах встречается для обработки ошибок гораздо чаще, чем setjmp.

 именно поэтому отказались от cow 

Неа. Отказались потому что это нарушало требование стандарта об инвалидации итераторов. Пруф: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=21334#c45

пользы от неё примерно 0

Выше приведен вполне реальный пример, когда благодаря CoW-строкам мужики неплохо ужались по памяти. Ну и во многих других языках (например, в Swift) CoW-строки вполне себе живут.

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

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

Отказались от cow потому что оно по всем параметрам хуже. Инвалидация итераторов это лишь малая часть

Простой goto в сишных проектах встречается для обработки ошибок гораздо чаще, чем setjmp.

goto не может заменить исключения. Его в С используют только потому что там нет RAII и нужно прыгать на clear_rcrs или что-то такое

У стандартизаторов есть такая мысль, что алгоритмы должны работать одинаково во всех случаях. Например, есть более эффективные в обычных применениях алгоритмы сортировки, а ещё можно было бы специализировать сортировку на конкретные типы численные типы и сделать её на них за O(n), но в С++ намеренно сделано не так. Сделано это из соображений стабильности (чтобы не было внезапных лагов как в языках с GC) и безопасности (чтобы нельзя было подобрать предельный случай и поломать приложение).

CoW-строками, потребляют слишком рандомное время на ряд операций. Банальное обращение к элементу может в зависимости от имплементации быть O(log N) или O(N), хотя пользователь ожидает O(1). Именно это стреляет по ногам джаваскриптерам при работе со строками (это же совершенно очевидно, что их нужно сплитить в массив по одной букве перед тяжёлыми вычислениями). Именно поэтому их нет в стандартной библиотеке С++, и не будет.

CoW-строками, потребляют слишком рандомное время на ряд операций.

Шаред_птры тоже потребляют рандомное время — деструктор может выполниться за несколько тактов, если счётчик ссылок ненулевой, и достаточно его декрементировать, и за много-много тактов, если надо прибить объект и вернуть память.

Про зависимость потребления памяти от того, выделен ли он через make_shared или нет, и говорить не стоит.

Работа на С++, конечно, не сахар, но хорошо что он развивается, тем самым облегчает жизнь.

Хотелось бы добавить пару слов об образовательной части C++ — это отличный способ углубиться в понимание устройства процессов железа, особенно после таких языков, как C# и Java. Более того, после изучения курса по алгоритмам и структурам данных, начинаешь более вдумчиво и ответственно писать код, предварительно успев отстрелить себе несколько раз ногу. Однако C++ не стоит рассматривать как первый для изучения ЯП. Для новичков лучше начинать свой путь с Python (можно без ООП), далее перейти на C# или Java, а затем уже погрузиться в мир указателей, динамической памяти и множественного наследования.

по поводу слайда сравнения используемых ресурсов (СО и прочего), вспоминается анекдот:

Жизнь слишком коротка, чтобы писать на ассемблере С++ (с)

Есть даже широкоизвестный закон Проебстинга: компиляторы увеличивают производительность кода в два раза каждые 18 лет.

Использование С++ не имеет никакого смысла, если размер задач не подразумевает миллионные убытки на электроэнергию и вычислительные мощности.

Зачем писать на C++ в 2022 году?

Потому что это лучший язык на земле?

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

В то же время одной из суперсил C++ является обратная совместимость.

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


Более того, что мешает скомпилировать объектники старым компилятором и подать их на вход новому линкеру? Думаю, их формат действительно совместимый, т.к. меняться там скорее всего особо нечему.

Есть там чему меняться

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

Обратная совместимость очень важная вещь. В каком нибудь линуксе PHP написанный на 5 версии, отказывается работать на 7 (следующей). Так быть не должно.

Вы видели процесс обновления С++ компилятора в большом проекте? Это месяцы работы большой специально выделенной команды. И с практически 100% багами в проде после перехода.

Это не совместимость.

Похоже, что зависит от степени запущенности проекта.

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

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

Тяжелее всего должен быть переход C++98 на 11, поскольку в древнем C++ очень многие вещи делались абы как и с явным уб, без которого жить было невозможно в принципе(те же потоки)

Ну я занимался процессом обновления C++ компилятора в умеренно большом проекте, в одиночку. Меняли MSVC на Clang, заметьте — кейс, мягко говоря, непростой. Не было там какого-то особого кошмара — процесс хоть и занял несколько месяцев, это было в основном время потраченное на согласование и тесты. По ходу дела благодаря миграции было найдено и исправлено несколько старых багов. После миграции в проде проблем не вылезло ни одной, ну и в целом там все либо работало, либо просто не компилировалось. Для сравнения — переход с Python 2 на Python 3 мы так до конца и не осилили, хотя там кода было меньше и он был проще — отчасти потому что хз было как тестировать старый и неизвестно кому принадлежащий код.

Питон 3 должен войти во все учебники как делать не надо. Хуже сделать наверно невозможно. Не надо равняться на худших.

Ну вот. Несколько месяцев. Примерно как я и написал. Рост трудозатрат от размера проекта совсем не линейный. Давно забытые куски которые в проде, но они никому не принадлежат, в больших плюсовых проектах тоже бывают.

Там проблемы сугубо организационного плана были. Менять надо было немного, но во всех проектах. Значит надо со всеми владельцами этих проектов это согласовать. Из thirdparty 80-90% библиотек не требовали никаких изменений, но 10-20% пришлось обновлять или патчить. Значит возня с обновлением пакетов в конане, после чего опять же ревью, аппрувы и тесты. Настроить боевой билд-сервер так чтобы он один и тот же проект мог по запросу собирать и одним компилятором и другим. Написать всем письмо о том что переходим на новый компилятор, подождать две недели просто на то чтобы оно до всех разработчиков дошло. Столкнуться с тем что несмотря на повторные письма кто-то все равно не поставил новый компилятор (причем из-за обратной совместимости у нас месяца два находились люди продолжавшие использовать старый компилятор и у них все при этом работало). Не думаю что всего этого можно было избежать при наличии необходимости менять вообще хоть что-то в коде.

Тогда искренне завидую вашей кодовой базе.

Ну там это вообще принято, не обеспечивать обратную совместимость, такой юникс-стайл )

Вы мне напомнили, как в универе нам однажды выдали пример интерпретатора на Си. На таком древнем Си, что он уже даже компиляторами не компилируется. Там типы аргументов объявлялись между ) и {, например.

Компилятор Rust поддерживает все три редакции в одно и тоже время (в разумных пределах конечно, например нельзя требовать от компилятора 2018 г., поддержку редакции 2022). То есть например с последним rustc 1.65.0 можно компилировать и писать в редакциях 2015, 2018, 2021.

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

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

что уж греха таить, иногда нельзя использовать что то из старого

Разве?.. Конечно, в новых редакциях нельзя, например, писать dyn Trait без dyn, но думаю речь не о таких ограничениях?

Если исключить из всех существующих программерских задач веб-разработку, то, собственно, какие ещё останутся пригодные языки, кроме C и C++?

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

Не могу, но это не значит, что язык не применяется в разработке. В Blender'е на нём скрипты, например, реализующие часть функционала. Есть игровой движок PyGame с мелким, но всё же какими-то реальными проектами (Frets of Fire), и в целом в различных игровых движках скриптовая часть нередко реализована на нём. Разнообразный научный софт часто написан на Питоне.

О, точно, Ren'Py же и всё на нём написанное, включая БЛ!
Ещё знаю Electrum и производные от него.
Вот уже целых две питонопрограммы. Ура.

Сложность питоноскриптов блендера надо посмотреть - вдруг они тоже реально что-то делают. Будет три.

Дополнительно вспомнил virt-manager и youtube-dl со товарищи.

sublime (был несколько лет назад вполне себе популярным текстовым редактором)

Только он написан на плюсах, а питон там используется для плагинов.

Всякие системные сервисы в Ubuntu достаточно широкоупотребимые программы?

Из таких, которые что-то делают, вспомнил fail2ban

а вы попробуйте снести весь питон с убунты (и второй и третий) а потом запустить какой-нить sudo apt

Тю. Так, наверное, и с выносом перла можно аналогичные результаты получить. Если не хуже.

Ха, а вы попробуйте после этого собрать какой-нибудь проект на Node.js!

Они даже не компилируемые.

Практически все и останутся

Хаскель, конечно. В светлом будующем — идрис.

Я готов согласиться, когда речь идет о плоских C. Но ОДНА база данных из всех (остальные не на плюсах) кажется мне вполне себе показателем.

Я конечно люблю кресты, но мне одному кажется (?) что вот это:

// теперь мы можем написать
auto isEven = [](auto number) { return number % 2 == 0; };
auto count = std::ranges::count_if(numbers, isEven);

Пример того как писать не надо? Ну окей, это короче, но совершенно нечитаемо... Может я застрял в embedded конечно, но выглядит гораздо хуже тривиального цикла приведённого выше.

Согласен, нечитаемо. Зачем отдельно объявлять isEven?
Вот так гораздо лучше:

auto count = std::ranges::count_if(numbers, [](auto n) { return n % 2 == 0; });

(благо, во многих проекта о maxcols=80 уже давно забыли как страшный сон)

Холивара ради: а ведь говорят, что у Rust ужасный синтаксис.

let count = numbers.iter().filter(|number| *number % 2 == 0).count();

Это на любом нормальном языке примерно одинаково выглядит.

Завидовать можно дотнетовскому LINQ. Вот он хорош и совсем не везде такой же есть.

Это на любом нормальном языке примерно одинаково выглядит.

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


Завидовать можно дотнетовскому LINQ. Вот он хорош и совсем не везде такой же есть.

Частный случай do-нотации.

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

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

Частный случай do-нотации.

Сделанная для людей, а не как обычно. И то много жалоб что слишком сложно.

Явно есть много где.

Везде, где есть анонимные функции, понятное дело. Но зачем их каждый раз писать явно?


Бойлерплейт это печально, но это терпимая плата за упрощение языка.

Самый простой язык — либо брейнфак, либо лямбда-исчисление, смотря из какого вы лагеря, но вот что-то ими особо никто не пользуется. Наверное, потому, что простота языка — далеко не единственный и даже не монотонный критерий.


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


map (`rem` 2) numbers

имхо проще, чем


map (\n -> n `rem` 2) numbers

Сделанная для людей, а не как обычно.

А do-нотация чем не для людей? Зато куда более общая концепция, и ей можно что в SQL-базу ходить, что парсеры описывать. На LINQ парсеры люди, конечно, тоже пишут, вроде даже на хабре статья была, но это уже совсем извращение ИМХО.


И то много жалоб что слишком сложно.

Это всё от, как там, интеллектуальной небрежности.

Наверное, потому, что простота языка — далеко не единственный и даже не монотонный критерий

Смотрю я на популярность Go и начинаю сомневаться.

Я за баланс. До уровня Go падать не стоит, а вот тот же С++ или Хаскель (смотря из какого вы лагеря) это перебор. Лучше всего посередине.

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

Пишут. Нормально выходит вообще. Опять не идеально, но нормально.

Для массового языка это достижение. На других массовых языках в среднем хуже выходит.

Это всё от, как там, интеллектуальной небрежности.

Мир не совершенен.

Смотрю я на популярность Go и начинаю сомневаться.

Популярность — это вообще вопрос такой, неоднозначный. Вокруг меня нет ни одного проекта на go, например, какие выводы можно сделать? Ну, да, какие-то там докеры-кубернетесы на нём написаны, кто-то что-то внутри компаний, говорят, пишет, но о популярности языка тут выводы сделать тяжело.


а вот тот же С++ или Хаскель (смотря из какого вы лагеря) это перебор

С C++ всё понятно, а с хаскелем что слишком сложно?


Пишут. Нормально выходит вообще. Опять не идеально, но нормально.

То есть, делать закат солнца вручную на неприспособленных к этому инструментах (метапрограммирование на темплейтах, парсеры на linq, etc) — это норм, а потратить вечер на специально созданную для соответствующих задач концепцию — сложно?

Популярность — это вообще вопрос такой, неоднозначный

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

а с хаскелем что слишком сложно?

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

Я на собеседованиях на Джаву уже отчаялся и больше не прошу собеседующихся на мидла своими словами рассказать мне как работает и что делает flatMap. Валятся в общем-то нормальные разработчики.

То есть, делать закат солнца вручную на неприспособленных к этому инструментах (метапрограммирование на темплейтах, парсеры на linq, etc) — это норм, а потратить вечер на специально созданную для соответствующих задач концепцию — сложно?

Ага. Я беру типичного мидла и отправляю его дописать очередной визитор в существующий проект. Со второго раза он точно справится. Две недели на обучение, наверно.

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

Как-то глобально что-то поменять в существующем проекте он не сможет ни в том ни в другом случае. Тут равенство.

А те кто могут и пишут просто смирились уже. В конце концов бывает и хуже. Люди вон на плюсах пишут такие же штуки еще и с мутирующим стейтом где-то глубоко внутри. Вот это настоящее страдание.

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

Количество вакансий тоже зависит. Вы ж не ищете просто вакансии «программист», наверное?


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

Если объяснять через всякие буррито, то и я бы не понял. Но для программирования это не нужно, всякие там монады — это просто интерфейсы. У вас же нет проблем с тем, что «IComparable — это такой-то интерфейс с такими-то требованиями»? Ну вот и «монада — это такой-то интерфейс с такими-то требованиями».


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


Не, я охотно верю, что есть люди, которые не могут осознать, что такое IComparable, как должны быть связаны сравнения и хешкоды, и всё такое, но что они делают в программировании? Зачем они вам нужны как нанимателю? Зачем вам как работнику работать среди таких людей? Зачем вам как клиенту пользоваться софтом, произведённым такими людьми? Похоже, языки, в которых невозможно делать вид, являются отличным фильтром!


Я на собеседованиях на Джаву уже отчаялся и больше не прошу собеседующихся на мидла своими словами рассказать мне как работает и что делает flatMap. Валятся в общем-то нормальные разработчики.

ЧТД, действительно был бы отличный фильтр.


Ну и, то есть, сама концепция flatMap (который есть и в Джаве, и в JS, и много где ещё) для них слишком сложная? Так тут не язык виноват, концепции-то есть вне языков.

Количество вакансий тоже зависит. Вы ж не ищете просто вакансии «программист», наверное?

Да. По настроению добавляю зарплата не указана и вычеркиваю все окологос конторы. Они не указывают зарплаты от бедности.

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

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

А функциональщина остается на уровне полугода Лиспа в лучшем случае. И то там не придираются к тому что человек не смог обычную курсовую написать. Когда пишут за него это хорошо видно.

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

Джейсоны должны быть переложены. Для этого не очень много знаний надо. Про квадрат и мапы знает, несложный код в режиме live coding написать может и ладно. И то люди даже без такого уровня знаний приходят регулярно.

ЧТД, действительно был бы отличный фильтр.

Для людей с сеньорами, архитекторами и еще много страшных слов в резюме на вакансию программиста я оставил этот вопрос. Даже из них отсеивается немало. Прям сразу за 5-10 минут все понятно.

Ну и, то есть, сама концепция flatMap (который есть и в Джаве, и в JS, и много где ещё) для них слишком сложная? Так тут не язык виноват, концепции-то есть вне языков.

Да. Скопипастить готовый кусок и немного подправить под свой случай могут. Объяснить как работает нет. Концепцию не понимают. Синтаксис пофиг. Я именно про концепции.

Концепции слишком сложные

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

Зато во всех остальных отношениях Haskell элементарен и прост.

И с ленивостью по умолчанию тоже вышло так себе.

Ну хз, практика показывает, что рассуждать о производительности в ФП вообще сложновато. Но человеку вон жсоны перекладывать надо, так что не страшно.

Ну и там с monomorphism restriction стрёмно немного.

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

И в целом compile-type prolog в тайпклассах это немного не то, чем должен заниматься человек, которому нужно код в продакшен писать.

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

Ну в джаве дженерики так-то тоже Тьюринг-полные

Так в Haskell и без Тьюринг-полноты оно недружелюбно. Без MPTC и FunDeps не типизируется и весьма простой код, а с ними как-то более хардкорно, чем хотелось бы ожидать от банальной перегрузки.

К счастью, есть -XNoMonomorphismRestriction

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

рассуждать о производительности в ФП вообще сложновато

Вообще практика показывает, что только в ФП и можно вообще о ней рассуждать, потому что как раз через equational reasoning можно выводить алгебраически (формализмом Бёрда-Меертенса, например) алгоритмы с лучшей асимптотикой, чем в спецификации, а без ФП можно только что-то эдакое наваять и понадеяться, что нигде нет ошибок.

Проблема только в конкретном сорте ФП, с нормальным порядком редукции, который меняет асимптотику от малейших правок и рандомно.

Почитал тут вашу ветку, и хочу кинуть камень в огород Haskell. Ни в каком другом языке я не встречал настолько отвратительной документации к библиотекам, как там. У плюсовых библиотек тоже часто с этим печально бывает, но обычно не настолько. Пример - дока по Lens - дается куча каких-то выражений на Haskell, и разбирайся с этим сам. Ну и нейминг зачастую как в математике - куча каких то одиночных букв и операторов, от которых глаза слезятся.

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

Без MPTC и FunDeps не типизируется и весьма простой код, а с ними как-то более хардкорно

Так «как-то более хардкорно» или «надо играть в пролог»? Окей, возьмём, что там у нас с MPTC и фундепсами сразу, ну возьмём какой-нибудь MonadError e m | m -> e. Где тут игра в пролог?


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

Банальной перегрузки чего?


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


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

В этом наборе слов нет смысла — здесь нет ни мемоизации, ни неявности, ни каких-либо синтаксических особенностей.


Коренная проблема в том, что в некоторых случаях type inference по стандарту должен вывести не наиболее общий тип (который был бы полиморфным), а зафиксировать конкретные значения метапеременных. Зачем так сделано — действительно непонятно.


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


Вроде бы как язык чистый, а тут какой-то стейт появляется.

Вас не смущает, что чистота — она в, собственно, языке, а стейт (который не стейт, а на самом деле вывод не-most-general-type, который в этой решётке оказывается, удивительно, не единственным) — в движке вывода типов?


Вообще практика показывает, что только в ФП и можно вообще о ней рассуждать, потому что как раз через equational reasoning можно выводить алгебраически (формализмом Бёрда-Меертенса, например) алгоритмы с лучшей асимптотикой, чем в спецификации, а без ФП можно только что-то эдакое наваять и понадеяться, что нигде нет ошибок.

А потом у вас η-редукция в компиляторе отвалилась, и всё, приплыли. Идрис — строгий, кстати.


Проблема только в конкретном сорте ФП, с нормальным порядком редукции, который меняет асимптотику от малейших правок и рандомно.

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

Где тут игра в пролог?

Очевидно, в том месте, где инстансы матчатся по типу, а не в определении класса.

Тайпклассы — это такой сахарок для передачи словарей функций + алгоритм их разрешения

Это и называется перегрузкой. Вам бы надо обязательно попросить Simon Peyton Jones тоже использовать расово верные эпитеты, а то у него иногда проскакивает. От недостатка образования в ФП, наверное.

Зачем так сделано — действительно непонятно.

Вас не смущает, что чистота — она в, собственно, языке, а стейт (который не стейт, а на самом деле вывод не-most-general-type, который в этой решётке оказывается, удивительно, не единственным) — в движке вывода типов?

Вас не смущает, что вы вообще не представляете, о чём говорите? :)

Ознакомьтесь, пожалуйста, с матчастью хотя бы в haskellwiki и спилите мушку.

Очевидно, в том месте, где инстансы матчатся по типу, а не в определении класса.

Если честно, не очевидно. Ну матчатся и матчатся — пока поведение достаточно ожидаемое, и пока вам не приходится лезть в дебри instance resolution'а, то никакой проблемы в этом нет.


А, кстати, когда вы какой-нибудь std::begin в плюсах пишете, вы не играете в пролог случаем?


Это и называется перегрузкой.

А когда я в идрисе пишу {auto foo : Bar}, что примерно то же самое с точностью до гарантий когерентности (и возможной неуникальности найденного инстанса), это тоже перегрузка, что ли?


Вам бы надо обязательно попросить Simon Peyton Jones

Апелляции к авторитету — ну такое.


Вас не смущает, что вы вообще не представляете, о чём говорите? :)

Меня не смущают несуществующие вещи. Ну, в смысле, я-то как раз понимаю, о чём говорю.


Давайте вы как-то будете по существу говорить, хорошо? А то нашли какой-то стейт, какую-то мемоизацию…


Ознакомьтесь, пожалуйста, с матчастью хотя бы в haskellwiki и спилите мушку.

А, это многое объясняет (включая другие некоторые ваши перлы)!


Вы, видимо, быстренько погуглили, что там в хаскеле неок, открыли для себя в том числе monomorphism restriction, сходили на ту самую вики, выцепили какие-то слова про мемоизацию, совершенно не понимая, причём она там и к чему… Так вот, хотя пересечение мемоизации (на самом деле возможности шарить вычисления, но неважно) и обсуждаемой «фичи», конечно, ненулевое, но очень-очень маленькое, и фокусироваться на этом не стоит.

это тоже перегрузка, что ли?

Любой случай, когда у вас имплементация при вызове выбирается на основе типа, принято называть перегрузкой. Спор о терминах -- ну такое. Можно с тем же успехом утверждать, что Java -- не ООП, потому что в ней LSP не работает, или что Си -- функциональный язык, потому что там есть first-class function pointers. Эта казуистика на практике бессмысленна, и вам ничем не поможет.

Апелляции к авторитету — ну такое.

Ну да, апеллировать к автору тайпклассов и HaskellWiki нельзя. Их только неудачники читают, чтобы вас публично уязвить.

Ну, в смысле, я-то как раз понимаю, о чём говорю.

Хорошо, давайте по существу, если Даннинг и Крюгер не позволяют вам разобраться самостоятельно.

Когда тайпклассы имплементят через dictionary passing, получается проблемка: вещи, которые раньше были просто значениями, становятся функциями. Например, когда мы пишем

fib = 0 : 1 : zipWith (+) fib (tail fib)

и для него (гипотетически) выводится тип (Num a) => [a], то на самом деле получается

fib d@{fromInteger, add}  = fromInteger 0 : fromInteger 1 : zipWith add (fib d) (tail $ fib d)

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

С тех пор, конечно же, ребята догадались, что мономорфизация/инлайнинг, в общем-то, не сильно хуже, чем dictionary passing, разве только с полиморфной рекурсией не работают, и GHC сейчас компилирует код иначе.

Мемоизация (на самом деле, CAF, но неважно), кстати, довольно часто используется, чтобы гарантировать, что какой-то код исполнился не больше одного раза, и вопрос "не выведутся ли тут типы, которые превратят этот код в экспоненциальный" внезапно становится крайне важным.

Вам бы пришлось узнать, как оно там на самом деле работает, если бы попробовали всё-таки сфокусироваться на этом и написать хоть какой-то тривиальный производительный код. На практике это полезнее, чем разбираться в тонкостях Has.

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

Ок, вывод аргументов — это тоже перегрузка, как и разные сорта proof search'а. Буду иметь в виду!


Ну да, апеллировать к автору тайпклассов и HaskellWiki нельзя.

Апеллируйте к тому, что говорится, а не кем говорится.


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

Да, я знаю, о чём и писал выше.


Например, когда мы пишем [...] исполняющийся за экспоненциальное время.

Во-первых, никто не мешает компилятору сделать из


fib :: Num a => [a]
fib = 0 : 1 : zipWith (+) fib (tail fib)

что-нибудь в духе


fib :: Num a => [a]
fib = go
  where
    go :: [a]
    go = 0 : 1 : zipWith (+) go (tail go)

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


это
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE ViewPatterns #-}

import System.Environment

fib1 :: Num a => [a]
fib1 = 0 : 1 : zipWith (+) fib1 (tail fib1)

fib1' :: forall a. Num a => [a]
fib1' = go
  where
    go :: [a]
    go = 0 : 1 : zipWith (+) go (tail go)

fib2 :: (a -> a -> a) -> (Integer -> a) -> [a]
fib2 add mk = mk 0 : mk 1 : zipWith add (fib2 add mk) (tail $ fib2 add mk)

fib3 :: (a -> a -> a) -> (Integer -> a) -> [a]
fib3 add mk = go
  where
    go = mk 0 : mk 1 : zipWith add go (tail go)

main :: IO ()
main = do
  [arg, read -> cnt] <- getArgs
  case arg of
       "1"  -> print $ fib1 @Int !! cnt
       "1'" -> print $ fib1' @Int !! cnt
       "2"  -> print $ fib2 @Int (+) fromInteger !! cnt
       "3"  -> print $ fib3 @Int (+) fromInteger !! cnt

Здесь fib3 и fib1' всегда исполняется быстро, fib2 всегда исполняется медленно, а fib1 исполняется медленно без оптимизаций и быстро — с ними.


В-третьих, это — половина истории, и иногда шаринг вообще-то бывает и вреден, например, потому что от него получается мусор в памяти, который GC не прибирает вовремя. Вон, смотрите, как люди приседают, чтобы этого не допустить.


И в этом на самом деле сложность — невозможно написать оптимизирующий компилятор для ФП-языка, который будет сам шарить когда надо, а когда не надо — не шарить. Ленивость тут ни при чём, иначе все бы давно делали {-# LANGUAGE Strict #-} и не знали проблем. Это я ещё в прошлом комментарии хотел написать, но забыл, увы.


С тех пор, конечно же, ребята догадались, что мономорфизация/инлайнинг, в общем-то, не сильно хуже, чем dictionary passing, разве только с полиморфной рекурсией не работают

И с конструированием этих словарей в рантайме (через костыли в случае хаскеля, конечно), но не суть. В чём суть — «ребята тогда не догадались, а сейчас догадались» для меня не является ответом на вопрос «нафига так делать», поэтому я и N комментариев назад и писал, что «непонятно».


вопрос "не выведутся ли тут типы, которые превратят этот код в экспоненциальный" внезапно становится крайне важным

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

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

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

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

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

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

У вас как-то интересно получается. То люди без развитых когнитивных навыков для возможности понять flatMap пригождаются (и это аргумент против «сложных» языков), то надо писать сложные алгоритмы бигдата хайлоад бизнес-логика (и это снова аргумент против «сложных» языков).


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

Ну да, не так давно статья про go пробегала, где от замены одного символа она в полтора раза быстрее работать стала. Проблемы довольно очевидны!


Отладкой проблем производительности в плюсах я вообще отчасти зарабатывал себе на жизнь некоторое время. Джава-люди вон тоже себе всё время то бенчмаркают что-то, то GC тюнят. Было бы всё очевидно — наверное, они бы этим не занимались.

У вас как-то интересно получается. То люди без развитых когнитивных навыков для возможности понять flatMap пригождаются (и это аргумент против «сложных» языков), то надо писать сложные алгоритмы бигдата хайлоад бизнес-логика (и это снова аргумент против «сложных» языков).

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

Ну да, не так давно статья про go пробегала, где от замены одного символа она в полтора раза быстрее работать стала. Проблемы довольно очевидны!

Отладкой проблем производительности в плюсах я вообще отчасти зарабатывал себе на жизнь некоторое время. Джава-люди вон тоже себе всё время то бенчмаркают что-то, то GC тюнят. Было бы всё очевидно — наверное, они бы этим не занимались.

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

Но это оптимизация критического пути или еще чего-то еще очень маленького от общего количества кода. В большей части кода это все не играет роли. Полтора раза от пары миллисекунд в каком-то методе на который нагрузка 1 рпс? Не интересно.

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

Писать могут разные люди, это да. Проект не без нормальных сеньоров.

Ну вот нормальные синьоры нормально и напишут.


А вот квадраты взрываются во всем коде.

Так обсуждаемые вещи вылезают плюс-минус настолько же редко, как критические пути. Примерно на уровне захвата тяжёлого объекта в плюсах в лямбду по значению, а не по ссылке, или чему-нибудь подобному.

Ну вот нормальные синьоры нормально и напишут.

Конечно. Но хочется чтобы язык им в этом не мешал.

Так обсуждаемые вещи вылезают плюс-минус настолько же редко, как критические пути. Примерно на уровне захвата тяжёлого объекта в плюсах в лямбду по значению, а не по ссылке, или чему-нибудь подобному.

Проблема в неявности этого. Глядя на код тяжелый объект можно увидеть. Критический путь обычно известен еще на этапе разработки и тестирования. А вот такая подстава от языка/компилятора может прилететь в любой момент в любом месте. Как ее увидеть заранее я пока не понимаю. Прям UB родной.

Но хочется чтобы язык им в этом не мешал.

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


Проблема в неявности этого. Глядя на код тяжелый объект можно увидеть.

Ага, когда у вас в лямбде написано [=] ..., или когда вы используете какую-то std::unordered_map, в которой может быть 3 значения, а может быть 300000.


Или компилятор может воспользоваться (N)RVO и не копировать строку/вектор/етц при возвращении, а может не воспользоваться и копировать, что даст O(n) вместо O(1).

Ага

Убедили. Бывает совсем неочевидно.

В итоге заменяем одни сложно обнаруживаемые проблемы на другие.

От всей функциональщины хочется гарантий, а не вот таких проблем. Гарантия корректности это великолепно. А можно еще гарантию неквадратности при прямых руках разработчика приложить? Ругань при компиляции с каким-нибудь ключиком подойдет.

В итоге заменяем одни сложно обнаруживаемые проблемы на другие.

Которых сильно меньше, и которые сильно менее опасны.


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

Можно. Ждёте -XDependent, заворачиваете интересные для вас неквадратные вещи в монаду (или что-нибудь подобное) и доказываете, сколько раз оно там вызывается.

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

Так Х-ь в продакшен тащить без надобности никто и не рекомендует. Посмотрите, где его большие компании используют.

Например, Github на нём сделал Semantic для эдакого IntelliSense, поддерживающего дофига языков. У них есть статейка "зачем хаскелл", в которой на много слов объясняется, что они бы на чём-то другом просто не смогли написать по причине большого количества букв. Гитхаб -- вполне бигдата.

Facebook'у нужно было разгребать тонны спама и прочего модерируемого контента, быстро модифицировать код под очередные хитрости спамеров и не ошибаться. Так нужно, что они автора рантайма Haskell наняли процессом руководить. Каждый пост на фейсбуке тоже звучит вполне как бигдата.

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

Лично мои аргументы, почему хаскеллю не место в проде, это инструментарий и инфраструктура. Под хаскелль нет нормальных средств разработки и отладки, и у него все плохо с библиотеками и фреймворками. Пробовал ради интереса создать простенький хеллоуворлд рест сервис, который умеет писать "хеллоу" в БД, так я с зависимостями и кофликтами версий разных либ прям запарился. И это при том, что я использовал stack, который решает кучу вопросов из этой серии. В общем хаскеллю сильно не хватает своего maven и своего spring boot. Ну и чего-то уровня Idea тоже хотелось бы.

Под хаскелль нет нормальных средств разработки

Это был отличный аргумент в 2010-м (тогда всё было очень плохо… правда, в C++ тоже всё было очень плохо, и если в хаскеле был cabal hell, то в плюсах даже этого не было), нормальный — в этак 2016-м/2018-м, а сейчас — ну, stack отличный, некоторые nix педалируют (хотя я бы не стал), для разработки — hls + ваш любимый редактор с поддержкой LSP. Например, мой сетап.


Я таки иногда пописываю (а раньше — много пописывал) что-то на плюсах, и на хаскеле это просто на порядки круче сейчас. C++ для прода не готов?


отладки

Не нужно.


Серьёзно, за все N лет разработки потребность в отладчике у меня возникала, наверное, раза два, и оба раза — чтобы выяснить, где какая-то левая функция кидает экзепшон. -fbreak-on-exception и :trace main в репле или что-то такое мне хватало за глаза.


и у него все плохо с библиотеками и фреймворками.

На hackage я нахожу практически всё, что нужно. К сожалению, на самом деле — нет повода запилить что-нибудь своё.


Пробовал ради интереса создать простенький хеллоуворлд рест сервис, который умеет писать "хеллоу" в БД, так я с зависимостями и кофликтами версий разных либ прям запарился.

servant + postgresql-simple — и работает отлично, и проблем нет. Что у вас не работало?


Кстати, как там в плюсах с этим будет? Плюсы для прода не готовы?

У плюсов есть CLion, у хаскелля только плагин к идее, и это лучшее, что я видел из средств разработки под Haskell.

Отладка - кому как. Мне нужно. Я хочу ставить брекпойнты в ИДЕ, смотреть данные вживую, и писать куски кода внутри окошка evaluate expression, чтобы там был весь текущий контекст типа подключения к БД и прочего. У хаскелля только консольный недоотладчик, интеграции с редактором не видел.

Простенький проект у меня не получился со следующими штуками: stack для сборки и управления зависимостями. Snap в качестве веб-фреймворка. HDBC для работы с БД. Data.Aeson для JSON сериализации/десериализации. Вот вроде основное, что вспоминается. Поглядел сейчас на stackage, в текущем срезе lts 20.2 Snap отсутствует, добавлял его руками, и в итоге получал конфликт версий Base в транзитивных зависимостях после добавления HDBC и еще нескольких либ.

P.S. Эталоном инструментов разработки для меня является Idea или VisualStudio + ReSharper. Сравниваю все остальное с ними.

У плюсов есть CLion

Который появился году в 2016-м, и года до 2018-го был неюзабелен, по крайней мере, для моих задач (а всякие рефакторинги там работают через раз до сих пор, вещи вроде go to definition тормозят и занимают секунды, и так далее). До 2016-го плюсы к проду были не готовы?


у хаскелля только плагин к идее, и это лучшее, что я видел из средств разработки под Haskell.

А также HLS для любого редактора с LSP, и оно куда лучше плагина к идее (который я тоже пробовал и даже до сих пор зачем-то финансирую разработчика). Зачем это пропускать мимо ушей?


Я хочу ставить брекпойнты в ИДЕ, смотреть данные вживую, и писать куски кода внутри окошка evaluate expression, чтобы там был весь текущий контекст типа подключения к БД и прочего. У хаскелля только консольный недоотладчик, интеграции с редактором не видел.

Это называется repl. Зачем вам отладчик, когда у вас есть полноценный repl?


Snap в качестве веб-фреймворка.

Но почему не servant? Он и приятнее, и меньше плясок вокруг него надо, и в stackage есть, и популярнее, и само вам даже js для доступа к API сгенерит.


Эталоном инструментов разработки для меня является Idea

Она до сих пор (clion, по крайней мере) даёт вполне ощутимый лаг при наборе текста. Я не могу всерьёз воспринимать IDE, в которой у тебя клавиатура будто в желе.

До CLion я для плюсов пользовался NetBeans'ом, но это было давно, в 2011-2015 примерно - писал всякие алгоритмы для аспирантуры. QtCreator довольно неплох, и тоже давно существует. VisualStudio мне лично не зашла, про нее ничего хорошего сказать не могу. Концепция редактор + плагины мне лично не заходит, хочу интегрированную среду разработки, чтоб вязл, и там сразу все есть. См. выше про идею.

Это называется repl. Зачем вам отладчик, когда у вас есть полноценный repl?

Вам вот заходит REPL, а я хочу отладчик. Хочу тыкать в IDE брекпойнты, смотреть как идет выполнение кода в разных случаях, видеть какие там реально данные за абстракциями спрятаны. REPL такой возможности не дает (или дает в неудобном для меня виде).

Но почему не servant?

Потому, что это был мой эксперимент с хаскеллем, и я захотел использовать Snap, деталей уже не помню, но чем-то он мне приглянулся. Возможно, понравилась их концепция снаплетов. С плюсами я таких проблем с зависимостями не помню, там вообще нечасто транзитивные зависимости мне попадались. Обычно цепочки зависимостей в плюсах, что я встречал, выглядели в стиле "библиотека для математики хочет blas и lapack, или что-то совместимое с ними".

Она до сих пор (clion, по крайней мере) даёт вполне ощутимый лаг при наборе текста.

Мне скорость именно набора вообще некритична, примерно 80% текста, что я пишу в IDE, она сама мне и генерирует, через всякие автоподстановки аргументов и прочие механизмы вроде Ctrl+Space, Alt+Enter и т. д. Мне больше важно, насколько хорошо IDE понимает контекст, и умеет в подстановку кода. Например написал obj->, получил список методов. Выбрал метод из списка, хочу подсказки по аргументам, да чтобы в соответствии с типами, именами и т. д. Или жмакнул хоткей на классе, получил заготовку юнит-теста для него.

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

Для прода готово!


QtCreator довольно неплох, и тоже давно существует.

Но менять таргеты для cmake'а нельзя до сих пор.


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

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


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

То есть, я правильно понимаю, что


хаскеллю не место в проде

потому, что вы решили провести


эксперимент с хаскеллем и [...] использовать Snap

которого даже нет в том же stackage LTS, и в результате вы ругаетесь на проблемы с версиями и то, что его нет stackage, и поэтому там нет фреймворков для реста?


Ну ок.


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

Ну а у меня конкретно в продакшене было, что одна библиотека требует C++03, а другая — C++11, и там бинарно несовместимые куски STL, поэтому получается весело.


Мне скорость именно набора вообще некритична

Речь не про скорость набора, а про скорость реакции IDE. Я нажимаю j и хочу видеть курсор на следующей строчке сразу, а не через пусть и небольшую, но ощутимую задержку.


примерно 80% текста, что я пишу в IDE, она сама мне и генерирует, через всякие автоподстановки аргументов и прочие механизмы вроде Ctrl+Space, Alt+Enter и т. д.

Это почти наверняка значит, что 90% из этих 80% можно выкинуть, поэтому вся эта автоподстановка не нужна.


Выбрал метод из списка, хочу подсказки по аргументам, да чтобы в соответствии с типами, именами и т. д.

HLS и это даёт. А тот же wingman те же методы вообще периодически достаточно успешно генерит на базе типов.

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

Для моего прода подходит, для вашего видимо нет.

которого даже нет в том же stackage LTS, и в результате вы ругаетесь на проблемы с версиями и то, что его нет stackage, и поэтому там нет фреймворков для реста?

Скорее претензия к тому, что я беру понравившийся мне фреймворк, понравившийся мне драйвер к БД и сразу, сходу получаю проблемы. В той же Java я тоже сталкивался с проблемами зависимостей версий библиотек, и их не очень хорошей совместимостью с определенной версией application server, но там я столкнулся с этим один раз за примерно 8 лет работы, а не вот так вот сходу.

Ну а у меня конкретно в продакшене было, что одна библиотека требует C++03, а другая — C++11, и там бинарно несовместимые куски STL, поэтому получается весело.

Как я написал выше, бывает даже в Java. Вопрос, сколько раз и за какой период вы с этим сталкивались.

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

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

Ну а возвращаясь к нашему исходному вопросу о пригодности х-ля для перекладывания жсонов — как часто при среднем перекладывании этих самых жсонов вам надо делать подобные Фибоначчи, завязывать узлы из двусвязных списков, и заниматься прочими непотребствами, где приходилось бы думать о том, что, когда и как именно будет вычисляться?

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

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

Но причём тут MR? Различное поведение байндинга вида a и C a => a — оно MR почти ортогонально. Ну, опять же, да, пересечение ненулевое, но напирать на MR, подразумевая вот это вот со словарями — это ну очень странно. Я бы так делал только в том случае, если бы хотел, чтобы собеседник меня гарантированно неправильно понял.


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

Например, как?


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

:) к вашему рефакторингу теперь комменты нужны. а иначе нужно голову включать что предикат делает

Программисту может быть непонятно, что делает 'return n % 2 == 0', серьезно? Я бы понял замешательство, если бы там хотя бы 'return n & 1 == 0' было написано...

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

Я бы на кодревью зарубил с комментарием "Надо переписать понятнее"

все таки есть разница между isEven и каким-либо выражением, независимо от его сложности

Согласен, нечитаемо. Зачем отдельно объявлять isEven?

Как представитель племени мамонтов, я бы все-таки предпочел сначала объявить isEven, хотя в нашем заповеднике это требует отдельного танца:

MODULE MyNumericTests
...
CONTAINS
...
  logical elemental function isEven(i)
  integer, intent(in) ::   i
    isEven=(mod(i,2) == 0)
  end function isEven
...
END MODULE MyNumericTests

Да, многословно... но обычно модуль типа MyNumericTests содержит не одну функцию, а целую кучу, а заодно и структуры данных, с которыми они работают (по сути, это очень похоже на описание класса). И он пишется один раз. Зато в программе будет:

use MyNumericTests
integer :: iArray(5)=(/1,2,3,4,5/)
...
... =count(is_even(iarray)) 
...

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

Желающие, конечно, могут обойтись и без isEven:

integer :: iArray(5)=(/1,2,3,4,5/)
integer :: counter
counter=count(mod(iArray,2) == 0) 

Но с точки зрения фортраниста (который сам когда-то писал GO TO), фразу "вот так гораздо лучше" применительно к этому коду стоило бы все же сопроводить тегом </сарказм>

Для холивара

А чтобы не слишком отклоняться от темы статьи, выскажу еще одно провокационное мнение: если речь идет о простом перемалывании чисел (и только о нем!), то "писать на C++ в 2022 году" действительно незачем ;-) Так как фортран позволяет написать то же самое и настолько же эффективно программисту гораздо более низкой квалификации. Ибо современный фортран явно проще, чем современный же С++. А главное, при одинаковом уровне программиста понятность (читаемость) фортран-программы будет получше за счет массивных операций и более тривиального синтаксиса. Ну и отлаживать фортран явно проще, если только не стрелять себе в ногу умышленно (впрочем, это на любом языке можно сделать ;-).

Рискну предположить, что застряли не в embedded (где вполне можно писать так же, причем еще и практически zero-cost, так как операции над рейнджами очень хорошо инлайнятся и оптимизируются как по производительности, так и размеру исполняемого кода, то, в чем C++ реально хорош и почему все готовы терпеть медленную компиляцию), а в процедурном подходе и старой версии стандарта.

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

Отнюдь, в современном JS как раз всё перечисленное (кроме может глобального состояния, но смотря что вы под этим понимаете) вполне привычно.

Однако, из того что вы перечислили "использование функций в качестве аргументов и возвращаемых значений", надежнее (что?) и читаемее код не делает никак.

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

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

Статья не поднимает один важный вопрос. Зачем начинать новый большой проект на C++, если есть Rust?

Затем, что новый большой проект на Rust будет компилироваться даже дольше С++, потому что ребята на голубом глазу накоммитили тайпчекер за квадрат от количества определений алгоритмом Робинсона. Именно это, например, не позволило использовать Rust в OpenBSD.

Вот это, кстати, очень интересно. Есть ли какие-то исследования насчёт времени сборки? Работал с большими проектами на плюсах и хорошо представляю как это больно

Сборка Rust проектов и правда очень долгая, но только если компилировать проект первый раз. Далее включается инкрементальная сборка, и по сути затык во времени идёт только на этапе линковки. Но и для этого есть решение — использовать линковщик mold вместо стандартного (подменяется одной строчкой в конфиге cargo). И вжух — проблема времени компиляции уходит в принципе!

И то, долгая она потому, что собираются все зависимости из исходников (а если совсем новая — то еще и скачиваются), в отличие от C++, где зависимости обычно все-таки уже скомпилированы. Пересборки собственного кода затем все же быстрее у раста, чем у плюсов.

Там претензия скорее в другом: кто-то предложил "а давайте всё на Rust перепишем!", при этом сам не желая что-либо делать. Ну и ещё у автора текста требование, чтобы компилятор Rust компилировался на i386, что, честно говоря, странное требование.

я читал coders at work и там говорили, что С++ слишком сложен. А не С/С++. И это писал человек, который каждый день 20 языков использует:-)

Немного отвлеченный вопрос про углеродный след и его снижение. Приведен даже слайд с разницей в 10 раз между платформой на С/С++ и платформой на пхп/питоне/руби. А где оценка углеродного следа во время разработки этой платформы? Видится мне, что по энергозатратам на разработку сравнение будет не в пользу С++.

Ну не знаю. У нас тут из похожих соображений UI у приложения запилили на C#. Бэк работает на пределе возможностей оборудования, поэтому его оставили на C++. Я долго уговаривал народ не городить огород и сделать фронт на Qt, но мне сообщили что Qt разработчиков найти сложно а C# легко, язык так крутой и мы все что захотим сделаем, поддерживает hidpi, можно вообще на аутсорс отдать… Итог лично мне не понравился. Вроде как C# должен быть гораздо проще C++ но лично для меня он не читается вообще, как и для большинства остальных разрабов. Набор функций который полумистическим образом связан друг с другом и наполнены boilerplate code. Найм C# оказался не проще найма C++, причем получили ситуацию когда разрабы нужны и те и другие и перебросить их между проектами проблема. Возможности UI мягко говоря не впечатляют (это при том что там по сути лишь несколько кнопок), проблем хватает, новый функционал добавляется оооочень долго. Возможно все это проблема с командой которая этим занимается, но идея-то была как раз именно в том что на C# команду нанять проще…

Сейчас на web собираются мигрировать, поскольку он кросс-платформенный, будет мол работать везде — хоть на десктопе, хоть на планшете. Уже уперлись в то что даже в UI-специфичном приложении использующем 3D половину вещей сделать сложно и оно тормозит. Ковыряют webassembly. А могли бы ведь с самого начала сделать Qt и все бы работало быстро и с минимумом переделок…
но мне сообщили что Qt разработчиков найти сложно а C# легко, язык так крутой и мы все что захотим сделаем, поддерживает hidpi

Это была очень странная информация, учитывая что направление десктопного UI в языке уже 6 лет как безнадёжно заброшено...

Дело было лет 8 назад, правда я повторно вопрос потом поднимал где-то 3-4 года назад когда начинали делать новый проект.

Вообще, на с++ писать не очень то и удобно. Рекомендую С#, это будет пожалуй получше чем С++. Кстати, можно и JavaScript компилировать в С# и использовать. 1 строка в js = 20 строк в С++

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

Вот как раз с ценой и сроками у проекта на C++ будут проблемы.