Comments 158
Дело в том, что это такие вещи, которые надо использовать очень аккуратно, важно не переборщить, не перейти границу. И, если я, скажем, могу для себя такую границу худо-бедно прочертить, то обязательно найдется тот, кто этого сделать не сможет и зайдет слишком далеко. К сожалению, сталкиваться с этим приходится гораздо чаще, чем хотелось бы.
Но кто будет добавлять механизмы по вывиливанию нула из твоего текущего старого проекта?
А зачем?
Если старый проект нет необходимости менять, то пускай там живет этот нулл. А если появился повод отредактировать, то можно даже руками Option вкрутить.
Ну допустим выпилите вы нулл из проекта. Пусть даже автоматически. Можете ли вы дать гарантию, что программа будет выполняться точно так же, как раньше?
Штуки вроде Option в языках вроде java приводят к дополнительной аллокации объекта, а это не самая дешевая операция. Чуть больше мусора в куче, чуть больше работы для GC. Но в целом программа уже не будет такой же.
Все пилюли так или иначе связаны со способами проверки на null. Но кажется, что проблема раздута, нет ни каких доказательств того, что эти пилюли действительно увеличивают производительность разработчика. На конференции Joker лет пять назад сотрудник JetBrains рассказывал, что они потратили сотни человеко-часов на добавление в код Idea аннотаций Nullable и NotNull. И он честно показал метрики по багам связанным с NPE за некоторый период до и после. Расхождение было в пределах погрешности: ошибок как было не много, так и осталось. Лучше бы он про них не рассказывал.
wait-free вполне себе нужен, когда идёт активная коммуникация между потоками. Особенно, когда это корутины в тредпуле. Возьмёт такая корутина мьютекс, да как уснёт и всё, приехали.
Это просто означает, что нельзя засыпать внутри критических секций. Причём тут wait-free — не очень понятно.
"Нельзя" — это полагаться на человеческий фактор. Предпочтительнее всё же, когда "невозможно". wait-free как раз позволяет обойтись вообще без критических секций и связанных с ними рисков и тормозов.
Вот только:
Во-первых, wait-free алгоритмы, как правило, довольно сложны — а потому куда больше подвержены человеческому фактору нежели критические секции.
Во-вторых, даже при использовании wait-free алгоритмов вам всё равно нужно следить чтобы в программе не было критических секций вокруг точек засыпания сопрограммы.
В-третьих, критические секции проверить можно и автоматически, благо некоторые языки это позволяют. А вот автоматическое доказательство корректности wait-free алгоритма куда сложнее.
Сами алгоритмы особой сложности не представляют и замечательно инкапсулируются в библиотеке, а не расползаются по всему прикладному коду.
Если не использовать критические секции — за ними и не надо следить.
Какие языки и что именно проверяют?
Сами алгоритмы особой сложности не представляют и замечательно инкапсулируются в библиотеке, а не расползаются по всему прикладному коду.
Вот только атомарность не компонуется, и вызов нескольких wait-free алгоритмов корректным wait-free алгоритмом может уже и не являться.
Если не использовать критические секции — за ними и не надо следить.
"Не использовать" — это полагаться на человеческий фактор.
Какие языки и что именно проверяют?
Ну вот Rust проверяет:
future returned bybar
is notSend
wait-free не про атомарность, а про отсутствие конкуренции за ресурсы. Так что всё прекрасно компонуется.
Линтер в помощь.
Спец проверка для проблемы, которая есть только у мьютексов — это, конечно, замечательно, но у wait-free данной проблемы нет вовсе.
Ну, вот пример корректного wait-free кода с сотней корутин в тредпуле:
static auto produce(Output!long numbers)
{
foreach (n; 10000000.iota)
numbers.put(n);
}
static auto consume(Output!long sums, Input!long numbers)
{
long s;
foreach (n; numbers)
s += n;
sums.put(s);
}
Output!long numbers;
Input!long sums;
foreach (t; 100.iota)
sums.pair.go!consume(numbers.pair);
numbers.go!produce;
long total;
foreach (s; sums)
total += s;
Ну да, этот частный случай wait-free каналов компонуется. Однако, это не делает компонующимися произвольные wait-free алгоритмы. Кстати, насколько я знаю у wait-free каналов есть проблема с чтением сразу из нескольких каналов.
И да, если уж вы заговорили об архитектуре "каналы и корутины" — то все мьютексы/критические секции прекрасно инкапсулируются внутри реализации каналов. К примеру, если заменить все каналы в вашем коде на каналы с блокировками — вы этого, скорее всего, вообще не заметите (пока не попытаетесь запустить программу на процессоре со слишком большим числом ядер).
Либо приведите пример, где что-то там не скомпануется, либо не сотрясайте интернет попусту.
Нет с этим проблем — обычный round-robin.
Ну да, кроме тормозов не замечу. Вы что доказать-то пытаетесь тут?
Рассмотрим операции чтения и записи разделяемой переменной. При наличии правильных барьеров они являются вполне себе wait-free алгоритмами.
А теперь попробуйте на основе этих операций увеличить переменную на 1 без гонок.
Вы что доказать-то пытаетесь тут?
Пытаюсь доказать, что "возьмёт такая корутина мьютекс, да как уснёт" не является той проблемой, для решения которой нужны wait-free алгоритмы.
Замыкания совсем другая история.
Почему?
Дарю.
Режим markdown в десктопном варианте, где количество символов > вначале строки указывает вложенность цитаты
В мобильной версии работает также, уже без всяких дополнительных режимов
Разработчики делятся на 2 лагеря — которые понимают что происходит, и которые не понимают. И я ни разу не видел, чтобы первые смогли обьяснить как читать код для вторых.
Согласно моим наблюдениям, разработчики из второго лагеря — это либо начинающие программисты, которые мыслят исключительно глобальными переменными (и у них не только с замыканиями, у них и с ООП, и даже со структурами данных проблемы) — либо те, кто по какой-то причине не хотят понимать как они работают.
файла на сотню строк, который состоит из 60-80 лямбд и замыканий
Не представляю зачем вообще такое может понадобиться. Тут явно проблема не в замыканиях, а в отсутствии стремления сокращать сложность программы.
С таким же успехом можно составить файл на 100 строк с 60-80 условными операторами, а потом всем рассказывать что условный оператор — плохо.
Ну или для замкнутых переменных можно использовать односимволный префикс типа $name
.
Нее, писать use в месте использования переменной — точно глупая идея. При низкой вложенности замыканий всё и без него видно, при высокой — он не поможет, потому что не говорит из какого скоупа эта переменная пришла.
Кстати, давать имена скоупам — не самая плохая идея.
Ну вот ниже я привёл самую громоздкую конструкцию из замыканий которую я смог придумать — декоратор, который превращает функцию в декоратор:
function declareWrapper(len) {
function (wrapper) {
return function decoratorFactory(...config) {
config.length = len;
return function decorator(fn) {
return function wrappedFunction(...args) {
return wrapper(...config, args2 => fn(...args2), args);
}
}
}
}
}
// использование:
@declareWrapper(1)
function withLog(name, fn, args) {
console.log(`${name} enter`, args);
const ret = fn(args);
console.log(`${name} exit`, ret);
return ret;
}
@withLog("foo")
function foo(x) {
return x+1;
}
Да, в этой функции может быть тяжело разобраться (особенно в том что она вообще делает). Но, тем не менее, неужели и правда не видно из какого скоупа какая переменная пришла, и обвешивание конструкциями вроде use действительно что-то тут упростит (без изменения остального кода)?
На мой взгляд, ваш тон — тон гомеопата. Гомеопатия не отрицает эффективность проверенных препаратов, но предлагает производить с ними нелепые манипуляции, считая, что эффект препарата с доказанной эффективностью переносится на молекулы воды, которые находились рядом с этим препаратом.
Аналогично, вы критикуете языки, но не доказываете, что ваши претензии являются значимыми. Например, если язык скрывает часть информации, достигая лаконичности, вы относите это к критическим недостаткам, которые приведут к потере денег заказчиком продукта. Проблема здесь в том, что вы не доказываете потери заказчика. Ваша логика основывается на том, что мы однозначно идентифицируем указанные вами недостатки, как нечто негативное. А вот то, что эти недостатки приводят к финансовым потерям заказчика — это уже ваша личная теория, не подкреплённая доказательствами.
Мы можем пойти дальше и найти пару примеров того, как гомеопатический препарат предшествовал выздоровлению пациента. Либо, найти пример того, как внедрение языка Go привело к потерям средств заказчика. Всё это будет равноценными логическими ошибками.
Думаю, что если прибегнуть к доказательствам, то выяснится, что для определённых проектов требуются языки с динамической типизацией, для других — со статической. Одним проектам нужны скоростные функции, написанные на Go, исовершенно не требуются объекты с классами. Другим проектам необходима компоновка сущностей в классы и интерфейсы. Это вполне нормально, когда температуру сбивают парацетомолом, а микрофлору кишечника восстанавливают линексом. Было бы странно решать обе задачи мукалтином и говорить, что парацетомол — безрассудная трата денег, если он только сбивает температуру, но совершенно не борется с кашлем. Собственно, все ваши тезисы сводятся к аналогичным выводам, только для ЯП. Нет, я не против доказательного анализа эффективности языков. Более того, такой анализ проводится разработчиками, которые планируют интеграцию нового языка в проект. Допустим, если сервер не справляется с нагрузкой, перевод АПИ с FastAPI (python) на Go может решить проблему, а близость синтаксиса языков покроет риск поиска специалиста по поддержке. Остаётся только оценить возможность нарастить мощность сервера и сравнить с затратами на внедрение языка. Полагаю, что случаи без оценки эффективности внедрения новых языков не являются системными.
Да мне кажется, что проблема в обратном — слишком многие хотят нового только потому, что оно новое. Ну или какие-то не слишком выдающиеся улучшения представляются прорывом.
А что именно "не так" с тем, что люди пробуют новое?
Это ведь и есть развитие, поиск идеала. Пусть не научный, а ремесленный. Но инструменты совершенствуются только благодаря таким энтузиастам, которые рискуют пробовать новое.
что без таких людей мы можем остановится в развитии.
Я не уверен, что согласен с этим тезисом. Ну в том смысле, что вот такие люди и есть те, кто двигают индустрию. По-моему, это не слишком связанные вещи.
Вон тут упомянули язык Го, который мне не слишком эстетически близок, но у него есть чёткий use case — «чтобы даже мартышка смогла». При этом его авторы люди немолодые, знающие и вряд ли склонные к экспериментам ради новизны. Однако же обеспечивают прогресс и новизну тем не менее.
Но без фазы «а давайте попробуем» го бы остался внутри гугла. Что и произошло с многими другими вещами у гугла.
И у гугла, и не у гугла. Чего там, это нормально. Открываем какой-нибудь rosetta code и видим, что он состоит в основном из совершенно экзотических или мёртвых языков.
А образом Вас втягивают?Ну так периодически приходится иметь дело с тем, что есть. Вас тоже втягивают. Ну вот самый банальный пример — сказал Эппл, что все теперь пишут на Swift, и вы либо больше не пишете под iOS (меняете профессию), либо пишете на Swift.
Это просто ради примера, у меня нет претензий конкретно к Swift, но да, вот так втягивание и происходит.
Я даже согласен с основной идеей — что полезность и новшества следует обосновывать.
Идеи проходят эволюционный отбор, неполезные новшества "вымирают". Всё, что используется, когда-то было полезным, и, вероятно, полезно сейчас.
Современные проблемы с утечкой памяти и недебажным многопоточным кодом — отличные примеры. Проблемы, которые раньше можно было исправить гораздо дешевле.
Частично эти проблемы от стиля кодирования. И утечки памяти, и запутанная многопоточность обходятся правилами и дисциплиной.
Конечно, эти проблемы также можно обойти сборщиком мусора и иммутабельными данными (как в эрланге), или borrow checker'ом (как в расте), или детектором утечек, etc. Но во времена, когда C++ становился популярными, эти штуки работали медленно (или отсутствовали), и на том этапе выиграл С++. Сейчас, спустя N десятков лет, и сборщики не так тормозят, и borrow checker есть. Конечно, это всё сейчас можно вернуть из прошлого, но тогда использовать не удавалось, приходилось соблюдать дисциплину на С++ (что получалось не во всех проектах).
ИТ индустрия несколько раз за свою историю повернула куда-то не туда. И теперь она приклывает слишком много усилий, чтобы вернутся на ту дорогу.
Уверен, для каждого "поворота не туда" были причины. Легко уверенно говорить "не туда" спустя десятилетия. Нельзя так уверенно сказать "не туда" для поворотов, которые делаются сейчас.
Мне кажется, если бы человека года так с 2007 по 2021 держали в плену и заставляли писать на Java 1.6 и EJB, то после освобождения и знакомства с современными концепциями и языками программирования, разорванный шаблон должен был бы породить текст как раз вроде этого.
Вроде бы уже прошло достаточно времени (7 лет с релиза восьмерки), чтобы даже самые отъявленные ретрограды поняли, в какую именно сторону развивается Java как язык, и поубавили градус хейта в сторону ФП.
Да примерно такое же, как и ООП, когда там нельзя даже вызывать методы у примитивных типов.
Докопаться можно до всего при желании.
Вы можете использовать JNI, чтобы использовать хвостовую рекурсию и ФП, которое есть в С++. Странен хейт в сторону явы, когда есть JNI, позволяющий писать часть проекта на компилируемом языке, например С++.
Ответы на ваши вопросы вполне просты и выводятся из теории рынка: в далёком прошлом ПО писали по-научному; ошибки в программах были, но это именно обычные человеческие опечатки/невнимательность, за каждой программой стояло математическое доказательство её работоспособности. Причины: компьютерное время было в дефиците по сравнению с временем программистов, поэтому запускать ПО с ошибками слишком дорого обходилось. А программистов было слишком мало по сравнению с населением Земли, поэтому порог вхождения был крайне высок (требовалось и знание математики, и машин, и алгоритмов...). Сейчас ситуация обратная: процессорное время слишком дешёво, программистов слишком много, ущерб, который испытывает бизнес от багов в ПО очень низкий, ниже, чем стоимость подготовки команды образованных программистов и их работы. Дешевле запустить 100 000 юнит тестов, чем вычитать код из хотя бы 100 строк и напрячь голову. Таким образом рынок продолжает двигаться и по сей день: программистов становится ещё больше, квалификация их становится ещё ниже, порог вхождения поддерживается на сбалансированном уровне за счёт количества спецификаций, которых надо учить, а ущерб, получаемый корпорациями от багов медленно растёт.
На счёт «изменить ситуацию». Только сегодня закончил исследование по связанной теме. Результат: писать надо на си, без плюсов. Парадигмы высокоуровневых языков огриничивают возможности программиста.
Вы серьезно полагаете, что перевод всего кода на си без плюсов уменьшит количество багов?
Почему именно на си, а не ада или что-нибудь с автоматическим доказательством корректности?
Что касается систем автоматических доказательств… помнится, теория алгоритмов гласит, что даже проблема остановки не имеет решения. Поэтому они могут найти какие-то ошибки и упростить их исправление, но окончательное заключение, что программа работает, делает то, что заявлено в ТЗ, и ничего более — должен делать человек.
Да без проблем, это же вопрос цены. В какой-то древней статье были оценки денежных расходов в расчёте на строку кода в разных проектах. И там оказывалось, что в софте для космоса и цены космические. Не потому, что там было что-то запредельно сложное, а потому что ракету не отдебажить и не пропатчить: надо всё под лупой вычитывать, а ещё бы хорошо иметь резервную систему от другой команды разработчиков.
Зато в обратную сторону: если есть конечный автомат, то превратить его в код на одном из популярных языков, не должно состоавить проблем.
если есть конечный автомат, то превратить его в код на одном из популярных языков, не должно состоавить проблем.Не совсем: если отталкиваться от верификаторов, в которых КА (абстрактные автоматы, темпоральная логика, Z-нотация) являются мейнстримной или околомейнстримной штукой, то полноценно оттранслировать их в код достаточно неочевидно, уж слишком они далеки от железа.
А я вот считаю, что представление всех систем как конечных автоматов только увеличит общее количество багов.
Обычно КА рассматривается не как вещь в себе, а как часть системы, включающей в себя, например, внешнюю память.
Так, если "добавить" к КА бесконечную ленту — получится машина Тьюринга, которая, вроде как, обладает тьюринг-полнотой.
Как-то так обычно и выглядят КА в реальной жизни, а не в фантазиях.
И насколько я понимаю, все вот эти расширения модели КА делают полученный результат нифига не верифицируемым и недоказуемым. Т.е. ничем не лучше любой другой программы, точно так же можно налажать. Разве нет?
Если добавить к КА память — то вероятно понадобится адресация, а значит адрес.Зачем? В С переменная действительно именованный адрес памяти, но в Хаскеле — именованное значение.
Затем, что мы про конечные автоматы говорили изначально, а все же не про хаскель. И их расширение путем добавления (динамической) памяти. Я согласен, что моя формулировка «вероятно понадобится адресация» наверное не совсем правильная — адресация понадобится, если мы будем добавлять память «как в C», а это наверное не единственный вариант.
Как вы себе представляете расширение КА памятью в виде именованных значений а-ля хаскель? Я вполне допускаю что такое сделать можно — но во всяком случае сразу не очевидно, как именно.
С самого начала — моя мысль была простая — что представить все системы в виде статического КА просто невозможно, потому что такой КА просто на это не способен (даже не вдаваясь в вопрос, будет ли это эффективно или там поддерживаемо). На что мне был дан ответ, что обычно КА это часть более широкой системы, включающей например память. И в этом виде с памятью он типа Тьюринг полный. Ну т.е., Тьюринг полный не статический КА, а некий его расширенный вариант, который в общем-то уже и не КА.
Так что там у Глушкова?
Так что там у Глушкова?Ну, не очень хорошо, умер даже :) На такой неконкретный вопрос могу только посоветовать глянуть его книгу «Синтез цифровых автоматов», предисловие и оглавление.
И насколько я понимаю, все вот эти расширения модели КА делают полученный результат нифига не верифицируемым и недоказуемым. Т.е. ничем не лучше любой другой программы, точно так же можно налажать. Разве нет?
Разумеется, именно потому я и написал что багов меньше не станет.
Весь мир пользуется. На большей части продакшен серверов есть. Влияние на безопасность всего и вся критическое.
И баг нашелся. Спустя много-много лет использования. Баг влияющий на безопасность.
Может не надо на С?
В sudo баг нашёлся: переполнение буфера. Сколько раз на всевозможных формумах обсуждалось, как избегать переполнения.
Рассуждая дальше, я бы выбрал политику +читаемость -низкоуровневая производительность. Каждый алгоритм стараться вместить в стандартные 25, 40, 80 или сколько строк нынче принято считать за 1 экран. Описание алгоритма должно умещаться в 1 тезис, иначе разбивать его на выполнение цепочки алгоритмов, возможно, с условиями и циклами. Обоснование корректности по стандартному списку из школьного курса: граничные значения, проверка ветвей, циклы, обработка ошибок. Написанный и проверенный код убираем под спойлер, забываем весь только что написанный код, и держим в голове (и на экране) только одну строчку с описанием алгоритма. Когда количество алгоритмов становится больше, то организуем их в структуру. N алгоритмов нижнего уровня убираем под спойлер, а над ними пишем верхний уровень из N/10. Если не получается уместиться хотя бы в N/5, то отбрасываем затею и ищем ошибку в архитектуре.
Таким образом на всём процессе разработки в голове необходимо держать только 1 страницу текущего кода и 1 страницу описаний алгоритмов предыдущего уровня. Это вполне реально. Если рассматривать пирамиду из всего трёх уровней и коэффициентом 5, получается 125 алгоритмов по 40 строк без повторений (5000 строк), среди которых вы сможете свободно ориентироваться не более, чем за 3 обращения к собственной документации. КПД такого кода вряд ли превысит 10%, но даже 1% от теоретического максимума было бы больше, чем мы имеем сейчас в популярном ПО.
Конечно, это всего лишь теоретическое рассуждение, не подкреплённое практикой, но… вдруг.
Пробовал хаскел (на что намек в комментарии).
Там на самом деле намёк на Idris, Agda и прочие языки с завтипами.
Хотя если считать аварийное завершение приложения хорошим выходом, то подойдёт любой современный язык кроме Си и С++ (причём последний "виноват" лишь в совместимости с Си).
Вы путаете наличие каких-то качеств и их безошибочную работу
Получается ли у вас писать хоть на каком-нибудь языке писать с первой попытки без ошибок, пусть даже с помощью всяких утилит?Если на каком-то языке программа заработала, пускай и со второго раза, то она будет работать и дальше, пускай и на ограниченном наборе данных.
Вы говорите про ошибки, которые расположены рядом, пускай и на соседних строка, когда глядя на них очевидно, что эти строки ошибочны. Например, если конкатенировать переменные с sql запросом, то очевидно, что они должны быть экранированы, и для этого хватит просмотреть условно пять строчек вверх. В то же время, в си ошибки не очевидны, и проблема заключается не в одной очевидной строке(вызов запроса с не экранированными переменными, а как минимум в двух не очевидно выглядящих. Например, переполнение буфера это две строки — выделение памяти и запись в этот буфер. Обе строки выглядят правильными сами по себе, так как это среднестатистический код на си, но в сочетании дают ошибку. Более того, даже если обе эти строки и верны, а потом, по каким-то причинам между ними мы вставили третью строку, которая либо перевыделяет память, либо меняет значение указателя, то только сочетание этих двух строк приводит к ошибке. Тут будет недостаточно просмотреть код на пять строк вверх, так как запись в буфер может быть в одной функции, создание буфера в другой, а вызов всего этого — в третьей, и соответственно нужно будет просмотреть сразу три функции, на одно простое действие. Никакой внимательностью или аккуратностью этого не достичь, так как это требует слишком большого объёма работы.
В sudo баг нашёлся: переполнение буфера. Сколько раз на всевозможных формумах обсуждалось, как избегать переполнения.Нужно всего лишь сделать пару тысяч прыжков по коду и прочитать в каждом из них условно от одной, до нескольких сотен строк, ради одной маленькой правки, да.
А си — на нём получается компактный код, и сам стандарт компактный. Что сильно облегчает доказательство.Размер стандарта влияет лишь на сложность реализации компилятора, и возможно библиотек времени исполнения. После того, как это написано, размер стандарта явно ни на что не влияет, вы же не пишете свой компилятор? Например, если добавить в си указатели с размером, то это усложнит стандарт, но упростит доказательство. Про компактность странно слышать — попробуйте, например на си написать аналог
new DateTime().format("Y")
и вам потребуется сразу три строки, вместо однй, две из которых будут связаны с управлением памятью. Если конкатенировать результат, то вам потребуется ещё как минимум три строки, в то время как в других языках это будет по прежнему хватит одной.В конце концов, точно также, как код на С транслируется в ассемблер, код на С++, JAVA, GO и прочих подобных языков может быть транслирован в эквивалентный им код на С. Но если так поступить, увидев результат вы скорее всего захотите выкинуть половину/90%/99.7% получившегося, просто как не выполняющего никакой полезной функции.
Любая достаточно сложная программа на C или Фортране содержит заново написанную, неспецифицированную, глючную и медленную реализацию половины языка Common LispЭто имеет смысл разве что на hello wold, на более сложных задачах вам всё равно придётся писать этот код самостоятельно. Так например, некоторые программы до сих пор не любят пути с юникодом и пробелами, так как программисты на си, вместо того, чтобы использовать библиотечные строки, которые нормально работают с такими путями, изобретают велосипед самостоятельно, и каждый самостоятельно исправляет в нём баги. И касается это не только строк, но и кучи других проблем.
На самом деле даже этого не хватит, либо функция экранирования должна быть идемпотентной, что дорого, если вообще возможно.
Нет, не возможно и не должна.
Так работают соглашения. По умолчанию предполагается, что никакая строка уровнем выше не экранирована. Если же такое экранирование находится — это ошибка, и она исправляется. И это, как правило, работает.
Для большей же надёжности можно для экранированных строк отдельный тип данных ввести.
Вы написали код на 2 строчке короче, чем в си, но какой ценой: создание объекта (наверняка со всякими защитами и кучей служебных полей), использование неявного свойства, что конструктор без параметров инициализирует текущей датой и временем, использование загадочного формата «Y», который не соответствует ISO 8601, в памяти зависает объект на неопределённый срок.
Дял ответа на ваш вопрос следует впомнить, что язык программирования — это просто инструмент, и создавался с определённым намерением. Если намерение человека не согласуется с таковым создателя, значит инструмент ему не подходит. Поэтому вопрос можно свести к тому «с каким намерением создавался тот или иной язык».
Проведя аналогичное размышление (https://habr.com/ru/company/timeweb/blog/551224/) можно сделать вывод, что «целевой» язык должен иметь возможность расширять свой функционал без превращения в новый язык. Логично предположить, что у такого языка должна быть какая-то базовая комплектация. Достаточно минималистичная. Но подождите, такой язык уже существует! Это си. Простенький синтаксис, стандартная библиотека на уровне «минималный набор для выживания» и возможность создавать неограниченное количество собственных библиотек.
Давайте тогда подключим какой-нибудь модуль с уже определённым объектом now, и напишем now->year. Полученное значение, разумеется, будет int. sprintf(buf,"%4d",now->year), я считаю, что писать так — я считаю неуважение к самому себе (и пользователям программы), поэтому подключим ещё парочку модулей и сделаем:
/* Предварительно запишем что-нибудь */
WriteC(stream_object,«Сегодня „);
/* Припишем текущий год в одну строчку */
WriteI(stream_object,now->year);
/* Ещё запишем что-нибудь */
WriteC(stream_object,“ год.»);
Согласен, что на с++ есть дополнительная изящность: stream_object<<«Сегодня „<<(now->getyear())<<“ год»;
но смысл не меняетя.
наверняка со всякими защитамиИ что в них плохого? У си практически для каждой вещи есть какое-то примечание и если человек его не прочитал, то он обретёт проблему на ровном месте. Если это распространённый проект, то скорее всего, что большинство таких ошибок уже найдены и с ними столкнуться не придётся, но ещё не факт. А непопулярные проекты — вообще кладезь подобных ошибок. Некоторые примечания не пишутся в явном виде, как например инъекция:
На другие языки программирования это не распространяется?В других языка можно увидеть память если не посмотреть в документацию?
но какой ценой: создание объекта (наверняка со всякими защитами и кучей служебных полей), использование неявного свойства, что конструктор без параметров инициализирует текущей датой и временем, использование загадочного формата «Y», который не соответствует ISO 8601, в памяти зависает объект на неопределённый срок.
Вы уверены, что во всех ста процентах случаев можно избежать выделения памяти в куче, например если это будет регулярное выражение или какой-то ещё более сложный объект и его нужно будет возвращать из функции? В примере ниже вы тоже создаёте now, разве это не объект? Вы полагаете, что неопределённый срок возможен только со сборщиком мусора, но на практике, это может возникать и при ручном управлении памятью. В гноме тоже есть(или уже поправили?) утечка памяти, которая кочевала из релиза в релиз, но до неё никому не было дела, все только переводили стрелки.
я считаю неуважение к самому себе (и пользователям программы), поэтому подключим ещё парочку модулей и сделаем:Скорее всего, вам придётся ограничиться своим кодом, а при интеграции с библиотекой, придётся использовать более низкоуровневый код.
WriteC(stream_object,«Сегодня „);Этот код очень зависим от контекста, и если потребуется запись в строку, например для последующей обработки, отправки в базу данных, то потребуется создавать отдельный буфер, что добавит ещё строки три(создание, получение си строки, освобождение). Возможно, эту строку нужно будет ещё раз скопировать уже в массив, либо придётся возвращать сразу уже буфер, что приведёт к проблемам так как другие функции принимают char*, а не StringBuilder*, либо деструктор буфера не должен освобождать строку…
Если в каких-то других языках для решения проблемы можно подключить какой-то пакет, то в си практически каждая проблема приведёт к необходимости написания шаблонного кода.
Проведя аналогичное размышление (https://habr.com/ru/company/timeweb/blog/551224/) можно сделать вывод, что «целевой» язык должен иметь возможность расширять свой функционал без превращения в новый язык.И в чём это заключается применительно к си? Это лисп можно расширить, за счёт его развитой системы макросов, или руби, за счёт его динамичности буквально во всём, когда прямо в коде класса можно вызвать код, который, например определит какой-то метод.
Ну а ежели такового нет, то я выбираю те языки код на которых компактный, предсказуемый и легко проверяемый. Вы можете что то предложить?
Парадигмы высокоуровневых языков огриничивают возможности программиста.
Возможности по стрельбе в ногу? Так в этом и смысл.
Мне весьма часто приходится вставлять код из высокоуровневых программ в микроконтроллеры и наоборот.
И возможно это вкусовщина, но возможность выстрелить себе в ногу весьма дисциплинирует. Ибо довольно странно видеть защищённый от стрельбы в ногу «Hello world» размером десяток мегабайт и исполняющийся секунды занимая при этом все 8 ядер.
Но, раз уж мы затронули эту тему, пару слов про ООП-диссидентов. Сложно сказать, что движет этими несчастными.
. С точки зрения доказательной медицины, препараты с недоказанной эффективностью — это мусор, который не имеет право называться лекарством.
Так ведь и для ООП и Java этого никто не делал.
Вы потом правда пишете, что
Реальность такова, что доказательство эффективности различных химических веществ — мероприятие крайне недешевое и доступно только Большой Фарме. А те не будут тратить деньги на лекарства, на которых они не смогут сорвать куш.
Ну так у IT и денег больше, чем у фармкомпаний и тестировать не так много языков нужно.
Эффективность парацетамола не доказана, и никто не знает, почему он делает то, что делает. Но помогает же!
Ну это откровенного говоря не правда. Испытали его давно. И не раз. И даже с необычной стороны.
А ведь можно войти еще дальше и учесть локальные обусловленные материальные особенности, то стандартизация вообще выглядит неприменимой. Наглядный пример это сформировать состав стандартной потребительской корзины для произвольного жителя планеты. Сразу видно что одинаковости не будет. В свою очередь потребительская корзина это огромная доля ежедневных мыслеформ, которые влияют на язык
А где, собственно, про доказательства? Эту бы обличительную энергию, да в нужное русло: в обзор современных методов и инструментов формальной верификации, использования зависимых типов, статического анализа…
Что там проблематично — о производительности рассуждать чуть сложнее, но это совсем другая история.
Насколько удобно там работать с изменяемым состоянием? Условно, есть два массива и в цикле мы вызываем
a[i].e -= 5;
b[i].h -= 10;
Теперь мы захотели изменять ещё и соседние элементы при каком-то условии.a[i].e -= 5;
b[i].h -= 10;
if(c) {
b[i - 1].h -= 10;
b[i + 1].h -= 10;
}
Насколько сильно изменится код, при том, что условие может зависеть от предыдущей итерации цикла?Посмотрел на примеры State
import Control.Monad.State
main = do
r <- runStateT (do
modify (+1)
(_, t) <- runStateT (do
modify (+1)
c <- get
lift $ lift $ print c
) 1
modify (*t)
s <- get
lift $ print s
modify (+3)
) 5
print r
Выглядит почти как с изменяемым состоянием, за исключением нескольких вещей. Во-первых изменяемым является только одна переменная, и чтобы узнать какая именно, то нужно подниматься к самому началу блока, и несколько непонято, как это масштабировать. Во-вторых, как я понимаю, мне нужно вручную следить за контекстом, и добавлять необходимое количество lift, что делает код довольно хрупким. В-третьих, переменная выглядит изменяемой только внутри соответствующей монады, и нужно либо растягивать самую внутреннюю монаду, включая в неё всё остальное, либо вводить кучу временных переменных.
Как я понимаю, даже без линейных типов создать двусвязный список будет несколько проблематично.
С доказательной медициной все проще. Как правило, проблема одна и четко формализирована. Нужно доказать что средство относительно безопасно и с определенной степенью эффективности решает проблему. С программированием это будет работать когда мы имеем четкие требования к приложению и они неизменны (детальное ТЗ, готовые макеты на все случаи жизни). В этом случае мы действительно можем выбрать максимально эффективный язык методом доказательного программирования. Но даже приложения в одной группе зачастую имеют сильно различные требования, проводить такой эксперимент на каждое будет слишком затратно.
Не вполне понимаю, зачем в данной ситуации писать, как всё плохо. Имхо, не плохо. Обычное развитие: новая идея кажется хорошей, её все используют, потом появляется идея получше, и все плюются на предыдущую, как плевались на пред-предыдущую. В чём смысл плакать о величине свалки идей?
Некорректно приравнивать программирование к медицине, авиации и прочим областям, где есть риск для жизней людей. "Умерший" проект можно переписать. Умершего человека — не вернуть. Отсюда и суровые требования к медицине, отсюда и отсутствие суровых требований к разработке. Они не нужны, только замедлят прогресс там, где будут введены. Введёте их — и отстанете от других. Только, пожалуйста, не пропихивайте этот бред на законодательном уровне, рекомендую сначала на себе опробовать (доказательность!).
Наверно, это проблемы авиации, что в ней используется "software provided as-is". Нужно что-то более надёжное — переписывайте. Глупо предъявлять к системе подсчёта лайков требования как к самолёту — это неэффективно, высокая надёжность не везде необходима.
Считаю, что обсуждать пост не надо, он самодостаточен и тем прекрасен :-D
А где абзац про ООП и джаву то? Как к ним относится доказательная медицина? Если продолжать аналогии — если джава лечит все болезни, то зачем вообще эта «альтернативная медицина»?
«Выживают» те ЯП, которые приносят большую прибыль.
Так что популярные ЯП уже доказали свою «правильность».
Грубо говоря «доказательство» происходит «здесь и сейчас» и «напрягаться» не надо «от слова совсем». ;-)
$result=universe($parameters);
$universe=new Universe($parameters);
Эта статья — не антитезис, а поток дерьма.
Автор хотел поблевать, автор поблевал. Делая вид, что это такой сарказм и сатира, претендуя на высокомерие и демонстрируя неуважение к коллегам.
Продираться через блевотину в поисках хотя бы вопросов, а не то, что ответов… так себе.
Я считаю, что подобным материалам не место на хабре.
Доказательное программирование