Были б проверки для математики "не так страшны", так бы не бились над языками и алгоритмами для исключения лишних проверок откуда только возможно. Ветвление само по себе плохо сказывается на оптимизации производительности, поскольку нужно предсказывать по какой ветви пойдёт вычисление, но ещё и сокращает эффективность кеша — больше кода, более рыхлый код, больше данных (информация о типе), соответственно меньше оседает в более производительном кеше процессора.
И одна к другой таких проблем у JS много, как бы ни был крут JIT, ухитряющийся иногда выжать производительность всего-лишь на порядок худшую, чем у нативного кода.
LLVM создавался как универсальный компилятор и в настоящее время имеет бэкенды для большого числа языков. Это своеобразный инструмент, позволяющий избегать велосипедостроения в достаточно сложной области — создании оптимизирующего компилятора под разные платформы.
Конечно, если смотреть на С/С++, для них существуют более эффективные компиляторы (тот же GCC для примера). Но эти компиляторы не так универсальны и их архитектура не позволяет их сделать настолько же универсальными, как и LLVM. Поэтому LLVM IR — такой себе лингва франка, в развитие которого вкладываются усилия многих сообществ и корпораций.
Это, к слову, о едином стандарте для разных языков: LLVM — один из кирпичиков этой универсальности. И такие языки как Python, получили свою дорогу в WAsm именно благодаря ему.
Многие языки просто делают обёртки вокруг системных библиотек. Сами системные библиотеки пишутся на разных языках, но предпочтение отдаётся тем, которые работают эффективней. Понятное дело, что большинство написано на C/C++, но есть примеры и библиотек на Фортране. Хотя, конечно, велосипедостроителей — тоже хватает. Но раз это устраивает соответствующие сообщества, то в чём проблема? Типовые наборы для платформ будут, как сейчас популярные библиотеки JS и веб-шрифты, грузиться с CDN и с большой вероятностью будут уже кешированы в браузере.
Go, Rust и Java самих по себе будет сложно подружить. Но как альтернативный язык решит проблему? Почему бы всем разработчикам не перейти на тот же Rust? Но они не хотят, их по тем или иным причинам устраивают родные Go, Java и прочие. То же самое и с каждым другим языком из длинного списка.
Ещё один язык как решит эту проблему? Будет ещё один язык, на который никто не хочет просто так переходить, тыкая своим богатым багажом наработок, накопившимся за десятилетия, в противовес молодой и малоразвитой экосистеме. Вот в чём весь вопрос.
Поэтому вопрос конкуренции языков никуда в ближайшей перспективе не денется. И его не решит дорогостоящий проект по созданию ещё одного самого лучшего языка, на котором не будет своих нужных модулей.
Причём даже его тотальная совместимость с тем же С и сишными библиотеками не решит проблемы языков, создававшихся, чтобы уйти от С. С++ — своя песочница, требующая написания специальной прослойки для совместимости с С. Rust — своя песочница со строгим контролем памяти, создававшаяся, чтобы избежать проблем С и С++. Обойма языков с GC — отдельная история — создававшаяся для избегания необходимости управлять памятью и проще писать код, даже серьёзно жертвуя производительностью. Какое из этих сообществ легко согласится пожертвовать своей песочницей в угоду возвращения к уровню С?
Здесь просто напрашивается картинка про 15-й стандарт.
Ну, будет ещё один язык, который будет точно так же бороться за место под солнцем с C, D, Go, Rust, C# (и далее длинный список выше не упомянутых, но относительно популярных языков от Паскаля до Хаскеля). Чтобы стать по-настоящему популярным, ему потребуется выйти за пределы Wasm и начать применяться в других областях (или рано или поздно найдутся энтузиасты, которые его туда притащат.
С другой стороны сделать качественный и непротиворечивый язык — это весьма сложная задача. И язык должен решать задачи программиста, а не задачи платформы, иначе ему ничего не светит. Дальше — задача распространения языка, обучения специалистов, создания необходимого множества библиотек (в современных языках это достаточно большое множество), создание своего менеджера пакетов, создание IDE (задача на самом деле не менее ресурсоёмкая, чем создание самого языка).
Вы уверены, что все эти затраты — действительно стоят того, чтобы получить лишь немного лучше подходящий для конкретной ниши язык, возможности которого с лихвой перекрываются уже существующими более универсальными языками?
Нет, я не против того, чтобы появился ещё один язык, который будет на голову выше своих конкурентов и станет языком будущего, затмив другие решения. Но я точно знаю, что это не простая инженерная задача, и в современных реалиях под силу лишь солидному и хорошо финансируемому коллективу, без очевидных гарантий успеха.
Посмотрите на пример Rust — его уже не один год разрабатывает достаточно крупный коллектив. Разработка идёт очень открыто в тесном сотрудничестве с сообществом и это сообщество делает немалый вклад в развитие экосистемы. Но совершенно очевидно, что даже при таком участии большого количества людей сделать качественный современный язык — достаточно непростая затея. Убедить людей перейти на него, когда их в целом устраивает их текущий язык, с которым они делают бизнес и сильно зависят от него, накопив большое наследие и опыт — непростая затея.
После этого можете оценивать шансы и необходимость ещё одного языка, а тем более его шансы стать хорошим языком.
А их никто не заставляет прописывать на каждый чих — в большинстве случаев они вполне подходят умолчательные и указываются только там, где ситуация нестандартна. Да и из практики скажу — не так уж они сложно читаются — просто ещё один вид параметров, достаточно легко отличимый от других и на ход выполнения программы не влияющий.
А что значит "выстрелил"? Язык применяется на практике, применяется успешно и получает признание в достаточно крупных компаниях. Если сравнивать его с Go, то придётся оценивать с точки зрения форы в несколько лет — Go начал применяться значительно раньше Rust и имел время завоевать популярность. Сравнивать нужно либо с соответствующим периодом в жизни Go, либо дать Rust столько же времени. А в целом — это языки всё-таки для несколько разных ниш, и то, что в одной нише кажется провалом, в другой вполне может быть несомненным успехом. Не стоит рассматривать эти языки как непосредственных конкурентов.
иногда цикл жизни проекта больше, чем библиотеки которую можно притащить из менеджера пакетов
Если взамен этого Вы будете вынуждены велосипедить свою библиотеку с той же функциональностью, только потому, что её проблематично добавить в проект, то почему тогда не взять библиотеку из менеджера пакетов под своё крыло, раз уж она стала не нужна первоначальным авторам?
"Паника" — это совсем не про штатную обработку ошибок, наоборот. Очень ресурсоёмкая операция не сравнимая с парой ассемблерных инструкций для проверки возвращённого варианта.
Поддержка многопоточности не означает, что вы должны ее использовать
Поддержка многопоточности означает, что мы можем её использовать и это будет "не больно".
Эти предложения только мне кажутся странными?
Что странного в первом предложении?
Во втором — таки кривой перевод.
«В Rust сложная система типов, которая позволяет, например, выявлять проблемы в случае конкурентного обращения к общим данным ещё на этапе компиляции».
об эмуляции некоторых аспектов ООП
Ходит как утка, крякает как утка :-)
Нет смысла спорить о самой правильной терминологии, для пользователей это выглядит как ООП, поэтому это всё равно будут называть ООП.
и тут же все портит излишне категоричным утверждением
Но нас же интересует вопрос косяков в дизайне, а не эмоции автора?
«требование чётко прописывать каждому типу значение по умолчанию»
могу объявить структуру указав типы для ее полей
По ходу речь не про типы, речь про значение по умолчанию для новосозданного типа. Эквивалентом в Rust была бы реализация типажа Default и конструирование нового типа через MyStruct::default(), однако Default реализуется далеко не для всех типов и его не всегда можно реализовать.
Автор наверное имел ввиду автоматический вывод типа переменной исходя из ее начального значения
Нет, речь идёт про факт инициализации переменной, независимо от её типа.
Rust позволяет объявить переменную заранее вместе с типом (например, в начале функции), но требует, чтобы значение было присвоено до первого обращения к ней.
Если ей не было присвоено значение, то соответственно оно не может быть и прочитано, для компилятора это однозначно очевидная вещь, не вызывающая разночтений.
Если говорить про выведение типов, то оно работает по уже объявленным типам (например, на входе/выходе функций, где тип опустить нельзя).
Если есть возможность разночтения — компилятор не примет этот код и потребует уточнить тип, поэтому ошибки здесь исключены.
Правилно сказать, что в Go отсутствуют шаблоны, в том виде, как это понимается в том — же C++. Более специализированные функции вполне реализуемы.
Ну, пол статьи о том, как не хватает этих шаблонов-дженериков, из-за чего приходится тупо копипастить код между более специализированными функциями.
кто вам сказал что Go является языком ФП?
Претензия автора как раз в том, что Go далёк от ФП, когда этого ФП в Go так не хватает.
А вот и не правда. В документации сказано [...]
Таки это не отменяет претензии автора, что авторам стандартной библиотеки позволено больше, чем всем остальным авторам. Авторы сторонних библиотек не могут повторить подобный фокус при нужде.
Но справедливости ради, стоит уточнить, что в Rust тоже есть подобие этой ситуации. Например, авторы стандартной библиотеки могут использовать специализацию на стабильном канале, в то время как это экспериментальная "фича" и всем остальным доступна только на ночном канале. Но в остальном, ситуация не настолько дискриминационная к сторонним авторам, поскольку использовать ночной канал не так уж и проблематично, да и экспериментальные фичи рано или поздно имеют свойство либо стабилизироваться, либо выпиливаться полностью, как не прошедшие проверку практикой.
вот и все объяснение
Что и является сутью претензии, а не теорией заговора.
Рискну предположить, что операции сравнения реализованы только для стандарных типов, вот и все объяснение.
Именно об этом речь и в статье — Вы не можете реализовать операцию сравнения для своих типов — «дискриминация» авторов сторонних библиотек.
В противовес приводится пример Rust, где это делается легко, реализацией для типа соответствующего типажа.
Не знаю, отложили ли авторы Go решение этого вопроса, но автор утверждает, что неплохо бы его таки решить.
«Модуль проверки типов, способный находить ошибки при компилировании, выступает в роли дополнительного тестового набора, который всегда проверяет каждое сочетание условий»
Допускаю, что Rust выполняет более тщательную проверку, но говорить о полном отсутствии контроля со стороны Go нельзя.
Тут опять же, речь немного о другом. В Rust тоже есть динамическая диспетчеризация, проверки в рантайме, и они несут с собой те же самые проблемы. Просто технически невозможно проконтролировать то, что заранее неизвестно (например, значение переменной из пользовательского ввода).
Но Rust предоставляет больше инструментов для статических проверок во время компиляции, применение которых он всячески поощряет.
Как недостаток автор приводит то, что в Go нельзя воспользоваться такой же стратегией исключительной проверки ограниченного множества вариантов и быть 100% уверенным, что в случае рефакторинга, когда добавляется дополнительный вариант в этом множестве, компилятор автоматически выявит все места, где проверка поломалась, поскольку перескок на дефолтную ветку — он технически корректен, и невозможно быть уверенным, что задумано было не так.
Претензия автора в том, что Go слишком уж полагается на проверки времени исполнения и это существенно сказывается на производительности и качестве кода, когда ошибки выпрыгивают во время работы лишь при определённых условиях, а не выявляются во время компиляции со 100% гарантией.
Да, go требует отдельного телодвижения, в виде запуска go vet ..., и не очень понятно, зачем это было сделано.
Подозреваю, что это связано с идеей быстрой компиляции. Анализ различных комбинаций очень быстро становится ресурсоёмким с ростом объёма кода.
Ну и если подсуммировать, то один из выводов можно свести к следующему: если инструмент прост в изучении и имеет низкий порог вхождения (именно так позиционируют сейчас Go), это не значит, что он будет так же прост в сопровождении развивающихся продуктов с растущими требованиями и сложностью. Здесь уже в комментариях прозвучал аргумент, что «язык, предназначен (по словам Роба Пайка) для написания сервер-сайд приложений на 100-150 строк кода». Возможно, что в этих условиях он вполне себе хорош, поскольку всё, что находится в таком приложении вполне себе можно удержать в памяти. Но обычно такие условия подразумевают тонну уже готовых "батареек" на все случаи жизни и большой выбор библиотек. И вот уже изготовление этих библиотек вызывает сложности, многие библиотеки достаточно быстро перерастают далеко за тысячу строк и нуждаются в обобщении типовых ситуаций в коде (привет дженерикам). А авторы стандартной библиотеки элементарно не могут «объять необъятное». Да и серверные приложения сегодня совсем не то, что они были лет 10 назад, когда могли рождаться основные идеи языка. Основная мысль автора — Go очень не хватает элементарных инструментов для качественного сопровождения крупных проектов, которые, вроде, не так уж и сложно внедрить, просто посмотрите на другие языки (go vet, возможно, может решить часть описанных проблем, но очевидно, далеко не все и не так исчерпывающе).
Но тут я не могу согласиться насчёт простоты изменения языка, а следовательно и ожидать реальных изменений после подобных статей. Если не совершить какого-то особого подвига при внедрении запрашиваемых фич, всё это на долгое время может стать точкой преткновения, когда комьюнити разделится на два крупных лагеря: один — с новым и эргономичным кодом и строгим контролем ошибок, а другой — с огромным наследием кода со старым подходом. Внедрить подобные фичи после того, как долгие годы отказывали им вообще в праве на существование и делали всё, чтобы их избежать — будет достаточно проблематично. Либо же радикально идти к Go 2.0, где "всё будет по новому, всё будет не так", и повторить печальный опыт Python 3, на десятилетие расколовшего комьюнити с до сих пор сохраняющейся ситуацией, когда некоторые библиотеки так и не были портированы на третью версию.
Go, как и большинство языков, так или иначе позаимствовал ряд концепций из других языков, существовавших до него. Концепции, реализованные в Rust — тоже не с нуля придумали. Внедрить пару удачных концепций, показавших свою высокую полезность на практике, и избавиться от неудачных, продемонстрировавших свои недостатки — это не значит сделать копию другого языка. В конечном итоге у Go есть свои принципы и цели, отличные от других языков, и предложенные концепции могут быть внедрены достаточно оригинально (и как минимум потребуют этой оригинальности и вдумчивой адаптации под уже существующие особенности языка).
Любой язык, транслируемый emscripten прекрасно будет компилироваться в WAsm.
Библиотеки на разных языках будут интегрироваться на уровне WAsm.
Но WAsm — не серебряная пуля, проблемы интеграции, создававшиеся специфичным рантаймом языков останутся. Одна из сложностей крутится вокруг разных подходов к управлению памятью.
Создание при этом отдельного языка — вопрос ортогональный. Сколько угодно — лишь бы хватало энтузиазма :-)
«ОС прямо в браузере» — сомнительная затея, проистекающая видимо из непонимания целей создания WebAssembly и закладываемых в него возможностей и ограничений. ОС будет просто «тесно» в таких условиях, да и смысла — 0, чем эта ОС будет управлять, если её саму держат в песочнице?
Assembly и Assembler — таки разные слова. Более того, это слова, достаточно широко применяемые в английском языке, а не страшно звучащие заклинания, как в русском. Странно было бы придираться к слову «сборка», если есть некий язык под названием «сборщик»?
WebAssembly — это больше про сам байт-код, а не про виртуальную машину с JIT. Стандарт кода, интеграция с другими API браузера, изоляция веб-приложений. Байткодовую машину тоже с нуля никто не изобретал — она уже есть в браузерах для JS. Теперь же это всё (или ещё не совсем всё) упорядочено и стандартизировано.
Я бы не назвал Rust «недостаточно выразительным». Скорее наоборот — недостаточно выразительным выглядит JavaScript на фоне Rust, как только начинает писаться что-то сколько-нибудь сложное. Rust позволяет писать достаточно сложные выражения лаконично и компактно. Хотя многие применённые в нём концепции непривычны широкому кругу разработчиков и на первых порах у новичка может получаться достаточно неуклюжий и громоздкий код.
Малый срок вполне может косвенно свидетельствовать о том, что, хотя в статье об этом ни слова, но таки присядет не один Муха, и это тоже можно считать за вполне удачный финал.
Дело даже не в наличии/отсутствии null.
Ссылки — это инструмент контролирующий совместный доступ к памяти, поэтому null для ссылки — в принципе лишён смысла.
Если нет переменной, то не нужна и ссылка.
При этом неинициализированные переменные (заранее объявленные) вполне допустимы, компилятор контролирует порядок исполнения и не даст скомпилировать код, читающий переменную до инициализации.
Другой момент — каждый раз создавать строку в процессе преобразований — не самый эффективный паттерн, хотя, конечно, так можно (например, функция будет каждый раз создавать String и возвращать её).
Но для ввода-вывода API строится так, что пользователь сам создаёт буфер и передаёт его для заполнения в функцию. Кроме прочего, это позволяет реализовывать эффективную конкатенацию и в случае необходимости переиспользовать буфер, без повторного выделения памяти в куче (достаточно «дорогая» операция).
При этом буфер может быть произвольного типа, лишь бы он реализовал соответствующий необходимый типаж Read или Write.
При таком подходе весь контроль над тем, что на самом деле будет происходить, — в руках программиста. И это достаточно важно для языка, предназначенного быть эффективным и позиционируемого как низкоуровневый/системный.
Хорошо, в данном случае вполне можно поместить школу владеющую списком учеников в Rc, а ученикам дать Weak ссылку на школу. Это вполне будет работать.
Но это сразу станет немного «дороже» — часть проверок перенесётся в рантайм, в программе придётся обрабатывать случаи, когда «что-то пошло не так».
У всего своя цена, серебряных пуль нет.
Поэтому в первую очередь ставится вопрос: зачем эта обратная ссылка на школу? Нужна ли она? Можно ли обойтись без неё? Какая стоит задача?
В конце концов, если уж очень нужно, никто же не мешает сделать структуру со школами и учениками на сырых указателях «как в старом добром Си» и работать с нею на свой страх и риск, огребая все щедро рассыпанные на пути грабли. Ссылки и времена жизни сделали ведь не для того, чтобы портить программисту жизнь, — это мощный инструмент автоматического контроля со статическими гарантиями.
Чуть выше я предложил использовать индексы или id — это не то же самое, что список пар «ученик-школа», упомянутый в статье. Тут уместнее предположить две владеющих структуры списком школ и списком учеников, а школы и ученики могут, если нужно, просто хранить номера или ключи в этой структуре.
Недостатки ссылок вполне перечислены в статье — ссылка может быть либо одна изменяемая, либо много — на неизменяемый объект. Меньше ссылок, связывающих состояние — меньше ограничений. Преимущество ссылки — более быстрое получение объекта при необходимости и статические гарантии, что объект существует.
Получение школы или ученика из таблицы по индексу — достаточно дешёвая операция, чтобы рассмотреть её как альтернативу хранению ссылки, при этом, если количество объектов небольшое, то можно сэкономить на размере хранимого значения и что-то выиграть с кешами процессора. Плюс к этому, номер в списке вполне можно применить к разным таблицам, хранящим разную информацию об объекте. Но у массивов и индексов в них есть и существенный недостаток, они плохо применимы к динамическим данным, когда списки активно меняются и элементы массово удаляются — здесь уже нужно решать вопросы с фрагментацией списков или обновлением индексов, что дорого. С другой стороны с указателями — это ещё сложнее, а со ссылками об этом можно забыть. Минус статические гарантии, плюс проверки и риск ошибок.
Как компромис в такой ситуации можно использовать {Hash,BTree}Map с идентификаторами, учитывая, что получить из него что-то по ключу — это уже более дорогая операция, чем обычный индекс. Так же без гарантий и с лишними проверками и рисками.
Rust, как системный язык, даёт разные инструменты для тонкого контроля над тем, как работает программа. При этом инструменты далеко не всегда взаимозаменяемые, каждый со своими требованиями, ограничениями, ценой, хотя может показаться «здесь куча разных способов сделать одно и то же». Поэтому ещё раз повторюсь: в первую очередь всё определяет задача, а уже дальше нужно думать, какой инструмент для её решения использовать. Делать что-то «для удобства», «так привычно» или «на всякий случай» — плохая мотивация.
«на вид-то он хорош, да зелен — ягодки нет зрелой» © :-)
Были б проверки для математики "не так страшны", так бы не бились над языками и алгоритмами для исключения лишних проверок откуда только возможно. Ветвление само по себе плохо сказывается на оптимизации производительности, поскольку нужно предсказывать по какой ветви пойдёт вычисление, но ещё и сокращает эффективность кеша — больше кода, более рыхлый код, больше данных (информация о типе), соответственно меньше оседает в более производительном кеше процессора.
И одна к другой таких проблем у JS много, как бы ни был крут JIT, ухитряющийся иногда выжать производительность всего-лишь на порядок худшую, чем у нативного кода.
LLVM создавался как универсальный компилятор и в настоящее время имеет бэкенды для большого числа языков. Это своеобразный инструмент, позволяющий избегать велосипедостроения в достаточно сложной области — создании оптимизирующего компилятора под разные платформы.
Конечно, если смотреть на С/С++, для них существуют более эффективные компиляторы (тот же GCC для примера). Но эти компиляторы не так универсальны и их архитектура не позволяет их сделать настолько же универсальными, как и LLVM. Поэтому LLVM IR — такой себе лингва франка, в развитие которого вкладываются усилия многих сообществ и корпораций.
Это, к слову, о едином стандарте для разных языков: LLVM — один из кирпичиков этой универсальности. И такие языки как Python, получили свою дорогу в WAsm именно благодаря ему.
Многие языки просто делают обёртки вокруг системных библиотек. Сами системные библиотеки пишутся на разных языках, но предпочтение отдаётся тем, которые работают эффективней. Понятное дело, что большинство написано на C/C++, но есть примеры и библиотек на Фортране. Хотя, конечно, велосипедостроителей — тоже хватает. Но раз это устраивает соответствующие сообщества, то в чём проблема? Типовые наборы для платформ будут, как сейчас популярные библиотеки JS и веб-шрифты, грузиться с CDN и с большой вероятностью будут уже кешированы в браузере.
Go, Rust и Java самих по себе будет сложно подружить. Но как альтернативный язык решит проблему? Почему бы всем разработчикам не перейти на тот же Rust? Но они не хотят, их по тем или иным причинам устраивают родные Go, Java и прочие. То же самое и с каждым другим языком из длинного списка.
Ещё один язык как решит эту проблему? Будет ещё один язык, на который никто не хочет просто так переходить, тыкая своим богатым багажом наработок, накопившимся за десятилетия, в противовес молодой и малоразвитой экосистеме. Вот в чём весь вопрос.
Поэтому вопрос конкуренции языков никуда в ближайшей перспективе не денется. И его не решит дорогостоящий проект по созданию ещё одного самого лучшего языка, на котором не будет своих нужных модулей.
Причём даже его тотальная совместимость с тем же С и сишными библиотеками не решит проблемы языков, создававшихся, чтобы уйти от С. С++ — своя песочница, требующая написания специальной прослойки для совместимости с С. Rust — своя песочница со строгим контролем памяти, создававшаяся, чтобы избежать проблем С и С++. Обойма языков с GC — отдельная история — создававшаяся для избегания необходимости управлять памятью и проще писать код, даже серьёзно жертвуя производительностью. Какое из этих сообществ легко согласится пожертвовать своей песочницей в угоду возвращения к уровню С?
Здесь просто напрашивается картинка про 15-й стандарт.
Ну, будет ещё один язык, который будет точно так же бороться за место под солнцем с C, D, Go, Rust, C# (и далее длинный список выше не упомянутых, но относительно популярных языков от Паскаля до Хаскеля). Чтобы стать по-настоящему популярным, ему потребуется выйти за пределы Wasm и начать применяться в других областях (или рано или поздно найдутся энтузиасты, которые его туда притащат.
С другой стороны сделать качественный и непротиворечивый язык — это весьма сложная задача. И язык должен решать задачи программиста, а не задачи платформы, иначе ему ничего не светит. Дальше — задача распространения языка, обучения специалистов, создания необходимого множества библиотек (в современных языках это достаточно большое множество), создание своего менеджера пакетов, создание IDE (задача на самом деле не менее ресурсоёмкая, чем создание самого языка).
Вы уверены, что все эти затраты — действительно стоят того, чтобы получить лишь немного лучше подходящий для конкретной ниши язык, возможности которого с лихвой перекрываются уже существующими более универсальными языками?
Нет, я не против того, чтобы появился ещё один язык, который будет на голову выше своих конкурентов и станет языком будущего, затмив другие решения. Но я точно знаю, что это не простая инженерная задача, и в современных реалиях под силу лишь солидному и хорошо финансируемому коллективу, без очевидных гарантий успеха.
Посмотрите на пример Rust — его уже не один год разрабатывает достаточно крупный коллектив. Разработка идёт очень открыто в тесном сотрудничестве с сообществом и это сообщество делает немалый вклад в развитие экосистемы. Но совершенно очевидно, что даже при таком участии большого количества людей сделать качественный современный язык — достаточно непростая затея. Убедить людей перейти на него, когда их в целом устраивает их текущий язык, с которым они делают бизнес и сильно зависят от него, накопив большое наследие и опыт — непростая затея.
После этого можете оценивать шансы и необходимость ещё одного языка, а тем более его шансы стать хорошим языком.
А их никто не заставляет прописывать на каждый чих — в большинстве случаев они вполне подходят умолчательные и указываются только там, где ситуация нестандартна. Да и из практики скажу — не так уж они сложно читаются — просто ещё один вид параметров, достаточно легко отличимый от других и на ход выполнения программы не влияющий.
Ну вот тема внезапно получила неожиданное развитие :-)
А что значит "выстрелил"? Язык применяется на практике, применяется успешно и получает признание в достаточно крупных компаниях. Если сравнивать его с Go, то придётся оценивать с точки зрения форы в несколько лет — Go начал применяться значительно раньше Rust и имел время завоевать популярность. Сравнивать нужно либо с соответствующим периодом в жизни Go, либо дать Rust столько же времени. А в целом — это языки всё-таки для несколько разных ниш, и то, что в одной нише кажется провалом, в другой вполне может быть несомненным успехом. Не стоит рассматривать эти языки как непосредственных конкурентов.
Если взамен этого Вы будете вынуждены велосипедить свою библиотеку с той же функциональностью, только потому, что её проблематично добавить в проект, то почему тогда не взять библиотеку из менеджера пакетов под своё крыло, раз уж она стала не нужна первоначальным авторам?
Разоритесь брать все вещи, вызывающие столько холиваров ;-)
"Паника" — это совсем не про штатную обработку ошибок, наоборот. Очень ресурсоёмкая операция не сравнимая с парой ассемблерных инструкций для проверки возвращённого варианта.
Поддержка многопоточности означает, что мы можем её использовать и это будет "не больно".
Что странного в первом предложении?
Во втором — таки кривой перевод.
«В Rust сложная система типов, которая позволяет, например, выявлять проблемы в случае конкурентного обращения к общим данным ещё на этапе компиляции».
Ходит как утка, крякает как утка :-)
Нет смысла спорить о самой правильной терминологии, для пользователей это выглядит как ООП, поэтому это всё равно будут называть ООП.
Но нас же интересует вопрос косяков в дизайне, а не эмоции автора?
По ходу речь не про типы, речь про значение по умолчанию для новосозданного типа. Эквивалентом в Rust была бы реализация типажа
Defaultи конструирование нового типа черезMyStruct::default(), однакоDefaultреализуется далеко не для всех типов и его не всегда можно реализовать.Нет, речь идёт про факт инициализации переменной, независимо от её типа.
Rust позволяет объявить переменную заранее вместе с типом (например, в начале функции), но требует, чтобы значение было присвоено до первого обращения к ней.
Если ей не было присвоено значение, то соответственно оно не может быть и прочитано, для компилятора это однозначно очевидная вещь, не вызывающая разночтений.
Если говорить про выведение типов, то оно работает по уже объявленным типам (например, на входе/выходе функций, где тип опустить нельзя).
Если есть возможность разночтения — компилятор не примет этот код и потребует уточнить тип, поэтому ошибки здесь исключены.
Ну, пол статьи о том, как не хватает этих шаблонов-дженериков, из-за чего приходится тупо копипастить код между более специализированными функциями.
Претензия автора как раз в том, что Go далёк от ФП, когда этого ФП в Go так не хватает.
Таки это не отменяет претензии автора, что авторам стандартной библиотеки позволено больше, чем всем остальным авторам. Авторы сторонних библиотек не могут повторить подобный фокус при нужде.
Но справедливости ради, стоит уточнить, что в Rust тоже есть подобие этой ситуации. Например, авторы стандартной библиотеки могут использовать специализацию на стабильном канале, в то время как это экспериментальная "фича" и всем остальным доступна только на ночном канале. Но в остальном, ситуация не настолько дискриминационная к сторонним авторам, поскольку использовать ночной канал не так уж и проблематично, да и экспериментальные фичи рано или поздно имеют свойство либо стабилизироваться, либо выпиливаться полностью, как не прошедшие проверку практикой.
Что и является сутью претензии, а не теорией заговора.
Именно об этом речь и в статье — Вы не можете реализовать операцию сравнения для своих типов — «дискриминация» авторов сторонних библиотек.
В противовес приводится пример Rust, где это делается легко, реализацией для типа соответствующего типажа.
Не знаю, отложили ли авторы Go решение этого вопроса, но автор утверждает, что неплохо бы его таки решить.
Тут опять же, речь немного о другом. В Rust тоже есть динамическая диспетчеризация, проверки в рантайме, и они несут с собой те же самые проблемы. Просто технически невозможно проконтролировать то, что заранее неизвестно (например, значение переменной из пользовательского ввода).
Но Rust предоставляет больше инструментов для статических проверок во время компиляции, применение которых он всячески поощряет.
Как недостаток автор приводит то, что в Go нельзя воспользоваться такой же стратегией исключительной проверки ограниченного множества вариантов и быть 100% уверенным, что в случае рефакторинга, когда добавляется дополнительный вариант в этом множестве, компилятор автоматически выявит все места, где проверка поломалась, поскольку перескок на дефолтную ветку — он технически корректен, и невозможно быть уверенным, что задумано было не так.
Претензия автора в том, что Go слишком уж полагается на проверки времени исполнения и это существенно сказывается на производительности и качестве кода, когда ошибки выпрыгивают во время работы лишь при определённых условиях, а не выявляются во время компиляции со 100% гарантией.
Подозреваю, что это связано с идеей быстрой компиляции. Анализ различных комбинаций очень быстро становится ресурсоёмким с ростом объёма кода.
Ну и если подсуммировать, то один из выводов можно свести к следующему: если инструмент прост в изучении и имеет низкий порог вхождения (именно так позиционируют сейчас Go), это не значит, что он будет так же прост в сопровождении развивающихся продуктов с растущими требованиями и сложностью. Здесь уже в комментариях прозвучал аргумент, что «язык, предназначен (по словам Роба Пайка) для написания сервер-сайд приложений на 100-150 строк кода». Возможно, что в этих условиях он вполне себе хорош, поскольку всё, что находится в таком приложении вполне себе можно удержать в памяти. Но обычно такие условия подразумевают тонну уже готовых "батареек" на все случаи жизни и большой выбор библиотек. И вот уже изготовление этих библиотек вызывает сложности, многие библиотеки достаточно быстро перерастают далеко за тысячу строк и нуждаются в обобщении типовых ситуаций в коде (привет дженерикам). А авторы стандартной библиотеки элементарно не могут «объять необъятное». Да и серверные приложения сегодня совсем не то, что они были лет 10 назад, когда могли рождаться основные идеи языка. Основная мысль автора — Go очень не хватает элементарных инструментов для качественного сопровождения крупных проектов, которые, вроде, не так уж и сложно внедрить, просто посмотрите на другие языки (
go vet, возможно, может решить часть описанных проблем, но очевидно, далеко не все и не так исчерпывающе).Но тут я не могу согласиться насчёт простоты изменения языка, а следовательно и ожидать реальных изменений после подобных статей. Если не совершить какого-то особого подвига при внедрении запрашиваемых фич, всё это на долгое время может стать точкой преткновения, когда комьюнити разделится на два крупных лагеря: один — с новым и эргономичным кодом и строгим контролем ошибок, а другой — с огромным наследием кода со старым подходом. Внедрить подобные фичи после того, как долгие годы отказывали им вообще в праве на существование и делали всё, чтобы их избежать — будет достаточно проблематично. Либо же радикально идти к Go 2.0, где "всё будет по новому, всё будет не так", и повторить печальный опыт Python 3, на десятилетие расколовшего комьюнити с до сих пор сохраняющейся ситуацией, когда некоторые библиотеки так и не были портированы на третью версию.
> бросил читать после 4 или 5 абзацев
Вы точно уверены, что «пустой файл» и «ошибка чтения» — это одно и то же? :-D
Разработчики антивирусов смотрят в недоумении и удивлённо пожимают плечами.
А разработчику зачем искать код вируса? Перезалил приложение и прописал права доступа как правильно, а не как всегда :-)
Библиотеки на разных языках будут интегрироваться на уровне WAsm.
Но WAsm — не серебряная пуля, проблемы интеграции, создававшиеся специфичным рантаймом языков останутся. Одна из сложностей крутится вокруг разных подходов к управлению памятью.
Создание при этом отдельного языка — вопрос ортогональный. Сколько угодно — лишь бы хватало энтузиазма :-)
«ОС прямо в браузере» — сомнительная затея, проистекающая видимо из непонимания целей создания WebAssembly и закладываемых в него возможностей и ограничений. ОС будет просто «тесно» в таких условиях, да и смысла — 0, чем эта ОС будет управлять, если её саму держат в песочнице?
WebAssembly — это больше про сам байт-код, а не про виртуальную машину с JIT. Стандарт кода, интеграция с другими API браузера, изоляция веб-приложений. Байткодовую машину тоже с нуля никто не изобретал — она уже есть в браузерах для JS. Теперь же это всё (или ещё не совсем всё) упорядочено и стандартизировано.
Ссылки — это инструмент контролирующий совместный доступ к памяти, поэтому null для ссылки — в принципе лишён смысла.
Если нет переменной, то не нужна и ссылка.
При этом неинициализированные переменные (заранее объявленные) вполне допустимы, компилятор контролирует порядок исполнения и не даст скомпилировать код, читающий переменную до инициализации.
Другой момент — каждый раз создавать строку в процессе преобразований — не самый эффективный паттерн, хотя, конечно, так можно (например, функция будет каждый раз создавать String и возвращать её).
Но для ввода-вывода API строится так, что пользователь сам создаёт буфер и передаёт его для заполнения в функцию. Кроме прочего, это позволяет реализовывать эффективную конкатенацию и в случае необходимости переиспользовать буфер, без повторного выделения памяти в куче (достаточно «дорогая» операция).
При этом буфер может быть произвольного типа, лишь бы он реализовал соответствующий необходимый типаж Read или Write.
При таком подходе весь контроль над тем, что на самом деле будет происходить, — в руках программиста. И это достаточно важно для языка, предназначенного быть эффективным и позиционируемого как низкоуровневый/системный.
Но это сразу станет немного «дороже» — часть проверок перенесётся в рантайм, в программе придётся обрабатывать случаи, когда «что-то пошло не так».
У всего своя цена, серебряных пуль нет.
Поэтому в первую очередь ставится вопрос: зачем эта обратная ссылка на школу? Нужна ли она? Можно ли обойтись без неё? Какая стоит задача?
В конце концов, если уж очень нужно, никто же не мешает сделать структуру со школами и учениками на сырых указателях «как в старом добром Си» и работать с нею на свой страх и риск, огребая все щедро рассыпанные на пути грабли. Ссылки и времена жизни сделали ведь не для того, чтобы портить программисту жизнь, — это мощный инструмент автоматического контроля со статическими гарантиями.
Чуть выше я предложил использовать индексы или id — это не то же самое, что список пар «ученик-школа», упомянутый в статье. Тут уместнее предположить две владеющих структуры списком школ и списком учеников, а школы и ученики могут, если нужно, просто хранить номера или ключи в этой структуре.
Недостатки ссылок вполне перечислены в статье — ссылка может быть либо одна изменяемая, либо много — на неизменяемый объект. Меньше ссылок, связывающих состояние — меньше ограничений. Преимущество ссылки — более быстрое получение объекта при необходимости и статические гарантии, что объект существует.
Получение школы или ученика из таблицы по индексу — достаточно дешёвая операция, чтобы рассмотреть её как альтернативу хранению ссылки, при этом, если количество объектов небольшое, то можно сэкономить на размере хранимого значения и что-то выиграть с кешами процессора. Плюс к этому, номер в списке вполне можно применить к разным таблицам, хранящим разную информацию об объекте. Но у массивов и индексов в них есть и существенный недостаток, они плохо применимы к динамическим данным, когда списки активно меняются и элементы массово удаляются — здесь уже нужно решать вопросы с фрагментацией списков или обновлением индексов, что дорого. С другой стороны с указателями — это ещё сложнее, а со ссылками об этом можно забыть. Минус статические гарантии, плюс проверки и риск ошибок.
Как компромис в такой ситуации можно использовать {Hash,BTree}Map с идентификаторами, учитывая, что получить из него что-то по ключу — это уже более дорогая операция, чем обычный индекс. Так же без гарантий и с лишними проверками и рисками.
Rust, как системный язык, даёт разные инструменты для тонкого контроля над тем, как работает программа. При этом инструменты далеко не всегда взаимозаменяемые, каждый со своими требованиями, ограничениями, ценой, хотя может показаться «здесь куча разных способов сделать одно и то же». Поэтому ещё раз повторюсь: в первую очередь всё определяет задача, а уже дальше нужно думать, какой инструмент для её решения использовать. Делать что-то «для удобства», «так привычно» или «на всякий случай» — плохая мотивация.