Обновить
2
0.5

Пользователь

Отправить сообщение

Эта интересная библиотека реализует множество концепций из статически
типизированных функциональных языков семейства ML, таких так Haskell,
Ocaml и Scala.

Не знал, что Haskell является языком семейства ML. Очень познавательно. Ссылочкой не поделитесь откуда это следует?

Я как понял, что меня клавиатура не устраивает (была какая-то старая logitech с чёрными кнопками) - на работе попросил MX keys, а домой купил механику Thermaltake (speed silver keys).

Так вот я с пол года приходил на работу - кайфовал от тактильных ощущений при печати. Потом приходил домой и опять кайфовал от других тактильных ощущений при печати.
Хорошая клавиатура - это просто приятно.

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

Придумали же американсие фирмы и взаимовыгодно с Китайскими фирмами сотрудничают.
А запрещает US Federal Government.

Придумайте пожалуйста аналогию, не содержащую искажения фактов.

А запрет на экспорт полупроводниковых технологий в Китай - тоже происки пропаганды?
Распространяемые теми, на кого США санкции накладывает?

https://www.bis.doc.gov/index.php/documents/about-bis/newsroom/press-releases/3158-2022-10-07-bis-press-release-advanced-computing-and-semiconductor-manufacturing-controls-final/file

Проблема в том, что чем дальше пользователь от бэкэнда оптимизирующего компилятора (middle-end в терминологии LLVM) тем больше он готов учить всех что надо делать.

Почему компилятор сгенерировал функцию, у которой нет инструкции возврата?

Компилятор - он про эквивалентные преобразования(*). Если в ф-ии нет возврата - значит и не задумывалось программистом и позволяется языком (LLVM - он бэкэнд ко многим языкам).
*) За исключением концепции UB - когда преобразования могут быть не эквивалентными. Поэтому и вопросы не к компилятору, а к UB.

Каждый раз будем спрашивать "if is_only_main_path() {...}" - попытка заткнуть решето.

Проблема именно в концепции UB и довольно злокачественном её использовании в погоне за +0.(0)х% производительности. Собственно от неё и надо бы избавляться.

DCE - важная и нужная фаза и для скорости компиляции и для качества результирующего кода.

Формально (с т.з. изучаемого в школе прескрептивистского подхода к грамотности) вы не правы и следовало бы писать в родительном падеже "1/е количества кандидатов".

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

Я вот честно вообще не понял что статья делает на Хабре. Там вместо информации сомнительные истории уровня ЖЖ.
Из информации - там:
- Платёжная система - это посредник между банками, чтобы им самим не договариваться.
- 1.5-2% стоимость перевода (и она как-то делится)
- как именно разбивается 16-значный № карты (криво и косо - про вычисление контрольной цифры ничего)
- Существуют, и могут отличасться от системы к системе MCC-коды.

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

А по-моему на второй картинке основной минус в том, что вы последовательно создаёте 4 разных продукта просто в рамках "создания VMP". Так ещё и вряд ли приобретаете компетенции по ходу дела (вряд ли умение делать велосипеды поможет вам при изготовлении автомобиля).

Чисто технически могло быть так (это пишут выше):
1. В компиляторе есть микро-фаза DCE (dead code elimination) - она удаляет весь код, до которого доказанно не дойдёт управление (и чисто транзитивно - весь доминитуемый "мёртвым кодом" код - тоже мёртвый).

2. Доказанно встретили UB - пометили цикл как DeadCode.

3. После какой-то фазы вызвали DCE - удалили сам цикл и всё что им доминировалось.

Какое именно поведение тут пытался сохранить компилятор?

Спасибо за вопрос. Он отличный.
Всё, что сделал компилятор - он сделал чисто технически боле-менее корректно:
1. Подкорректировал main (удаление return - несомненно malevolent действие) из-за UB, право имел.
2. Сохранил never_called (она не статик => её чисто теоретически мог вызывать кто-то другой извне).
3. Сложил 2 функции в машинных кодах в исполняемый файл, легли подряд.

Вы спросите: так бю... а виноват кто.
Виновата концепция UB, которая делает FAIL-SLOW (т.е. она явно говорит - что-то может пойти не так, как угодно плохо, но до конца пытается сделать вид, что UB это не ошибка, и может ещё удастся вырулить и всё наладится) - и в итоге в плохом случае портит вообще всё.

Сейчас эту проблему осознали. В новых языках, даже низкоуровневых UB нет. Например Rust - даже по производительности он не медленнии С\С++.

Проблема со старыми языками. Там от UB избавитсья крайне сложно.
Малой кровью - надо было бы запретить return в этм месте. Но тут надо копать кишки LLVM.

Цикл while (true) {} — это бесконечный цикл без сайд-эффектов, то есть
UB.… Можно просто удалить весь последующий код вместе с return'ом.

Вообще конкретно это UB довольно логично и служит для помощи программисту.
А вот компилятор, так зловредно его эксплуатирующий... ну выпускнику MIT надо в резюме воткнуть строчку "мой коммит в LLVM ускорил specperf_*** на 0.01%.

Логика тут примерно такая:
1. Любой код без side-effects можно (и нужно в целях производительности) удалять.
2. Кроме бесконечного цикла - который сам по себе side effect.
3. Но если мы не можем доказать про цикл, что он конечный/бесконечный (и цикл без side-effects) - давайте его удалим. Пользы намного больше чем вреда.
3.1 при этом явно скажем программисту, что бесконечные циклы без других side-effects запрещены

============ а вот дальше идёт довольно-таки плохая логика
4. а давайте удалять те циклы, которые без side-effects даже явно бесконечные, формально ссылаясь на то, что это UB и его быть в коде не должно.
4.1 В оправдание п.4 - c развинием компиляторов многие циклы про которые раньше было непонятно finite\infinite стало можно сделать выводы. Поэтому если не принимать "4" - то для некоторых других существующих программ с обновлнием компилятора получим изменение поведения. В общем как не крути всё плохо.

ПС
> для бесконечного цикла надо давать команду ожидания окончания потока.
В Rust (примерно) так и сделано.
Цикл loop {} именно бесконечный с гарантией что не удалится бэкэндом.
В С \ С++ часто сложно сделать что-то разумное не поламав обратную совместимость.

never_called не была помечена как static.
А значит обязана остаться в единице компиляции.

ПС
Но вообще требовать от компилятора удалить неиспользуемый код довольно странно.

Спасибо.

Один интересный вопрос остался не покрытым.
В С и С++ разные наборы UB - как бэкэнд LLVM это разруливает?

Я просто зеркалю ваши вопросы по C.

При этом совершенно игнорируя суть вопроса (если мы хотим донести до людей "почему"):
- для обучения С вам надо ссылать на то, чего студенты ещё не знают (потому, что это середина курса "архитектура ЭВМ"
- для обучения Python это ссылка на только что пройденное начало "курса ООП".

Python не является ОО-языком (а Волга впадает в Каспийское море).

Ну и что? На объяснение разницы уйдёт пара минут. И оно будет относится к уже понятным вещам (в то время как при объяснении С вам постоянно придётся ссылаться на непонятные вещи).

ПС
> это я просто косплею ваши...

Косплеите неконструктивно.
В Си например из одного #define можно квиз устроить - но разумный человек найдёт компромис, что для обучения достаточно самых простых объяснений, с чем я и согласился. Вы же пытаетесь быть максимально неудобным в каждом вопросе - в итоге вместо "сильной позиции" вы показываете себя "неудобным собеседником".

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

Вот вы действительно не понимаете того, что вы пишите? Почему в С надо объяснять дольше и неудобнее одни и те же концепции?

Всё просто: в ООП-языке вы попали на 2-3 первых лекции по ООП. Дальше ваши студенты знают что такое ООП и вы говорите (ну это так устроен объект в нашем ООП языке). И есть куча способов изучать ЯП и ООП параллельно без проблем.

А в случае С вы попадаете на середину курса "архитектуры ЭВМ". И какого-то хорошего способа изучения С, без отвлечения на посторонние темы (или оставления студентов без объяснения) я не вижу.

P.S. Как все эти пункты связаны с утиной типизацией?

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

Плохая документация объясняет "что", хорошая "почему".

Вы предлагаете писать учебник, как плохую документацию.
Из личного опыта - в 10 классе я не понял С, как второй ЯП (первым был Pascal) ИМХО ровно потому, что нам объясняли "что" без "зачем" (в целом я думаю что вся наша компьютерная подгруппа не поняла - во всяком случае никто им не стал пользоваться как ходовым инструментом). Потом пришло время - и понял.

При этом есть места, где это будет "умеренно плохо" (define \ main) а есть места где очень плохо (printf); include - где-то на границе.

Т.е. с define \ main можно поступить на первых порах как с "отрезанием сосисочных жёпок" (запомните и делайте так, потом объясним). Хотя делать как обезьянака потому, что так принято - это прям супер не по программистски.

А вот с printf - вы просто создаёте у ученика диссонанс и противоречие.
Вот вы объяснили типы, аргументы и параметры ф-ии, приведение типов.
И тут делаете printf, которая просто противоречит всему сказанному. Человек хочет разобраться - просто потому, что у него хорошее программистское мышление а ему "опа не твоего ума дело или это займёт пару лекций".

Хорошее правило написания документации - не пишите "что" пишите "почему".
Преподавать "что без почему" для #include и main() - не смертельно но плохо (нас не научили C, а это был второй язык - отчасти поэтому).

А вот для printf("") - это же epic fail. Это же реально магия.
Везде надо передавать либо точноый тип, либо приводимый, либо (void *).
А тут вы прямо передаёте произвольный тип в функцию, функция "понимает". Как это вообще сделано? Где границы применимости? Как это использовать?....

отличный выбор, как по мне

Согласен.

Это С здорового человека

Мне кажется вообще не стоит бросаться такими фразами.
Начиная с того, что Go - конкурент С++ и в каком месте он конкурирует за нишу С вообще не понятно. Продолжая... (хотя зачем продолжать если Go не конкурент за нишу С совсем).

Информация

В рейтинге
2 125-й
Зарегистрирован
Активность