Комментарии 37
Интересная мысль делать сверку имен в рамках правил сонар куба например.
Но много краевых проблем когда будет куча варнингов по именовани или надо сильно усложнять код. Например случай когда ищем пару объектов из базы и потом с ними чтото делаем - оба должны быть в одном контексте для работы и как они тогда должны именоваться? Туда же чтото более сложное по физ/мат вычислениям где имя подфункции и название переменной связаны условно в рамках формул.
Вы правы для общего языка. Но данный паттерн будет применять исключительно для подмножества C++, который находится в разработке. Этот DSL(над С++) запрещает проблемные паттерны (общие имена, синонимы), требует явных структур вместо кортежей, и ограничивает область применения (не для мат. вычислений). Хотя даже для кортежей данное правило применяется свободно, но только в данном случае я разбирал конкретно семантику исключительно C++.
Вот как звучит правило для картежей:
Для картежей правило наименование становится тем же, и применяется к каждому аргументу
sender, recipient = find_sender_and_recipient()
# Придётся писать:
def find_sender_and_recipient() {
user1, user2 = find_users() # внутри можно
sender = user1
recipient = user2
return sender, recipient
}В данном случае sender, recipient и return sender, recipient совпадают. Правило применяются. Если вы имели ввиду что-то другое, уточните вопрос :)
вот этот момент, что это идеи для самостоятельного DSL лучше было бы в статью как раз внести, теперь стало понятнее.
пример с кортежами тоже ясен, как раз это имелось в виду под усложнением кода, т.к. требуется много бойлерплейта для упаковки/распаковки, чтобы все имело имена.
еще как вижу появляются области, где ваши правила не действуют. Туда же как понимаю подпадает необходимость как-то оборачивать работу с подключаемыми либами, чтобы при работе семантика заработала
Да, концепция на то и концепция чтобы подать идею разобрать частные случае, немного вникнуть в особенности. Краевые случаи и способы решения, всегда конечно можно раскрыть. А вот что по поводу с подключаемыми библиотека - для них существуют свои пространства имён, которые уменьшают вероятность конфликтов
вот этот момент, что это идеи для самостоятельного DSL лучше было бы в статью как раз внести, теперь стало понятнее.
Если я не ошибаюсь, то это есть в статье:
Данная идея интересна как исследовательская концепция и могла бы быть реализована в:
Доменно-специфичных языках (DSL) — где строгость именования критически важна
Образовательных языках для обучения строгому стилю программирования
Экспериментальных языках
эти нейрослоп статьи замыливают восприятие информации подаваемой в таком же стиле со списками и выделением ключевых моментов. Вот то что написано в комменте выше вполне однозначно указывает на экпериментальность и что она возможно даже уже применяется в прототипе, проще было бы так и написать изначально
В моём DSL варнинги по именованию — это часть языка. Система заставляет явно описывать семантику через имена. Если получается 'куча варнингов' — значит, в коде было 'куча неявностей'.
так.
У меня 2 юзера и я хочу сравнить их айди.// Единственно правильный вызов:auto user_id = get_user_id(user1) // ✓
а второй как?auto user_id2 = get_user_id(user2) // нарушит твою конвенцию имен
А если он не юзер, а админ, и я хочу это подчеркнуть?
Можно использовать кортежи: К примеру
auto [user1_id, user2_id] = get_user_ids(user1, user2); // Функция возвращает кортежПравило остаётся, но есть контролируемые исключения для реальных сценариев.
И мы усложняем код только для того, чтобы переменные были фиксированные. Убрали сложность в одном месте, но добавили в другом (и, ИМХО, больше).
а что если надо так:
auto cur_user_id = get_user_id(get_cur_user());
auto cur_group = find_group_for_id(cur_user_id);
auto creator_id = get_user_id(cur_group.creator);
auto creators_group = find_group_for_id(creator_id);
for(auto other_user: cur_group.users)
{
other_id = get_user_id(other_user);
...//далее в коде используются все предыдущие id, например:
if (other_id == cur_user_id)
continue;
...
Есть варианты:
<sarcasm on>
С++-style преобразования
name_cast
auto cur_user_id = name_cast<cur_user_id>(get_user_id(get_cur_user()));Дополнительные функции для преобразования:
auto get_cur_user_id(auto user_id) { ... }
auto cur_user_id = get_cur_user_id(get_user_id(get_cur_user()));Введение неявных преобразований имён:
user_id может быть преобразован в
<прилагательное>_user_id<числительное>_user_id
то есть разрешены конверсии user_id => current_user_id, second_user_id, other_user_id, power_user_id
а такие конверсии запрещены user_id => price_id, mail_and_user_id
<sarcasm off>
Очень интересная задумка
user_id может быть преобразован в
<прилагательное>_user_id
получается что-то типа венгерской нотации. Хорошо что статическая типизация избавила нас от необходимости хранить в каждом имени переменной еще и тип.
Допустим я всё именую в snake_case, а автор очень нужной мне библиотеки - в camelCase, а остальные библиотеки тоже используют snake_case, как предполагается такие проблемы решать?
Никак. В этом и пока что и заключается строгость контрактов. Можно "выдумать" механизм переопределения. Но на данный момент эта концепция остаётся строгой. У вас есть полное право под себя подстроить так, как вы хотите
Просто это довольно странно. Нужно либо всех обязать использовать какой-то один регистр, либо дать возможность переопределить имена. Иначе, очень вероятно, что люди просто забьют и будут использовать библиотеку с camelCase именами в проекте с преимущественно snake_case именами. Обычно даже строгих проверок делать не надо, достаточно просто конвенции прописать. В тех же ruby и go чаще соблюдают snake_case и camelCase соответственно, хотя ни интерпретатор в случае ruby, ни компилятор в случае go никак этому не препятсвует.
Абсолютно с вами согласен. Но я решил на данный момент отталкиваться от абсолютной строгости. Я не решил вводить "полумеры" в начале пути, потому что пришлось бы привести овер дофига пример/контраргументов. Эти полумеры в начале пути я посчитал избыточными. Я не против развить эту идею. Данная концепция не приватизирована каким-либо образом
Ваша идея с переопределением хоть и хороша, но я не представляю понятия как это ввести не нарушая строгость правила. Теоретически можно написать обёртку, которая возвращает эту функцию напрямую, определив в атрибутах. Но это нарушает правило, так как в правилах заложено, что необходимо до конечной точки вызова. Так как это требует анализа потока данных, то можно ввести ограничение на такие вызовы с явным наименованием. Но я буду рад, если вы предложите альтернативы
Я больше верю в обозначение, какой регистр более предпочтительный: snake_case или camelCase, как в решение проблемы. Переопределение сложно тем, что функций в библиотеке может быть много, а переопределять имя каждой - не самое приятное занятие. По идее переопределение можно сделать с помощью специальной функции переименования. Где-нибудь в файле с определением зависимостей пользователь может задать функцию на вашем же языке, которая будет принимать и возвращать строку, и явно указывать, что данная функция должна переопределять имена для всех функций библиотеки X. Но это костыль по-моему.
Единый кодстайл принудительно
Как быть если совпадут имена результатов из разных функций? Нельзя их вызывать из одного места.
Или высокий уровень сложности: имя результата одной библиотеки совпадает с типом из другой.
А можно пойти ещё дальше и требовать точного совпадения ещё и аргументов функций, тогда не получится перепутать ширину и высоту. Правда, половина кода уйдет на переименование переменных и жонглирование областями видимости: const auto& arg = result; повсюду
и требовать точного совпадения ещё и аргументов функций
Это тоже планируется ввести
Правда, половина кода уйдет на переименование переменных и жонглирование областями видимости:
const auto& arg = result;повсюду
Не придется, так как для существующих языков это сломает обратную совместимость. А для новых - принудильный стиль программирования
Вот уж, придумать несуществующую проблему, а потом её криво "решить".
Если прям хочется, настройте LLM в code review для проверки семантики имён переменных
Проблема существует в legacy-проектах с разросшейся кодовой базой. LLM — хороший костыль, но они работают с уже существующим разнобоем. Предлагаемый подход — попытка предотвратить разнобой на системном уровне. Мое предложение — не замена код-ревью, а формализация соглашений. Так же как const формализует неизменяемость, а типы — структуру данных. Не для всех проектов, но для некоторых критически важных систем — возможно, оправданно.
legacy-проектам ваш язык никак не поможет. Да и проблемы таких проектов бесконечно далеки от неудачного выбора имён
legacy-проектам ваш язык никак не поможет.
Я сказал что существует проблема в легаси-проектах. Читайте внимательно, пожалуйста.
Да и проблемы таких проектов бесконечно далеки от неудачного выбора имён
Вы недооцениваете проблему, потому что рассматриваете её в парадигме «стиль кода». На деле это проблема семантической целостности распределённой кодовой базы, которая в legacy проявляется особенно остро. LLM — паллиатив, который работает с симптомами. Мой подход — попытка лечения причины через формализацию контрактов. Вы говорите: "Зачем строгие имена, если есть LLM?" это тоже самое что как говорить в 1995 году: "Зачем нам статическая типизация в Java, если есть хорошие тестировщики?"
Вы знаете, есть интересный парадокс в нашей индустрии. Те, кто начинал в 90-е (и я в этом уверен, у вас богатый опыт), выработали железобетонный рефлекс: "Если работает — не трогай". И именно поэтому вы так скептически смотрите на предложения "всё переделать по-новому". Потому что видели, как "новые правильные подходы" разбивались о реальность дедлайнов, ограниченной памяти и процессоров, которые сегодня кажутся игрушечными.
То, что было вынужденной необходимостью в вашу эпоху - сегодня стало культурой технического долга.
Вы смотрите на моё предложение и видите: "Ещё одна умная идея, которая разобьётся о реальные проекты". Потому что ваша реальность 20 лет назад — это C++ без STL, без RAII, где каждая лишняя проверка — это просадка производительности.
Но иногда — очень редко — приходит время, когда "революционная идея" становится "стандартной практикой". И переход происходит именно тогда, когда меняется поколение разработчиков, не обременённое паттернами мышления предыдущей эпохи.
Обсуждать мою реальность, мою эпоху, мой бекграунд, мои рефлексы и паттерны предыдущей эпохи как-то не очень интересно.
Так что желаю вам удачи и творческих успехов.
Вы начали с тезиса "несуществующая проблема". Когда я показал, что проблема существует в крупных системах, вы решили закончить диалог.
Теперь, когда технические контраргументы закончились, вы прячетесь за "неинтересно обсуждать".
Дискуссия закончена не потому, что проблема несущественна, а потому, что вы предпочли защищать своё эго вместо анализа аргументов.
Жаль. Настоящая инженерия начинается там, где заканчивается догматизм. Удачи вам в рамках вашей картины мира.
Различные сущности выделять в различные типы.
Использовать аннотированные (именованные) параметры функций.
Но С++ для этого не очень подходит - аннотирование отсутствует (можно чуть обойти в С++20), отсутствует перегрузка по возвращаемому значению, слишком легкое автоприведение типов.
оверинж нейроманьяка, который не только сам не умеет писать нормальный код, так ещё и пытается усложнить жизнь другим.
1) Начинать надо с правильных названий функций, это ещё важнее:
чтоб в названии функции был возвращаемый тип,
типа:
FindUser()
CreateUser()
FindUserAndGroup()
ещё лучше:
Find_User() с подчёркиванием т.к. таких Find итак уже сотни, но некоторые не рекоммендуют смешивать стили snake_case и CamelCase
2) когда решите вопрос (1) с правильными названиями функций,
только после этого можно переходить к правильным названиям переменных
Данные название были выбраны для пример :/. В данном случае можно бесконечно угодно спорить как правильно назвать. Ровным счётом я могу сказать что подходит названия get_db_player() и так далее. Это были примеры, а не финальная реализация. В таких случаях имена выбираются для наглядности, а не для соблюдения всех соглашений. Критиковать учебный или иллюстративный код за имена функций — странно, честно говоря. Либо тогда, возможно, стоит перечитать, что такое «пример», и зачем он вообще нужен. Мои навыки писать код проверяются не по именам в демонстрационном фрагменте.
Как мне кажется тут больше проблема в auto, с прямым указанием типа проблема просто исчезает
Useruser = get_user(user_id)
Useradmin = get_user(admin_id)
if(user=admin){
hack_pentagon()
}
Идея автора статьи хороша, а проблема актуальна. Требовать в линтере или CI/CD чтобы было user1 = get_user() - вполне разумно. В python/javascript мы легко это порешали регулярками по строке, в скрипте, запускающем автотесты. Дилемма snake/Camel оказалась первопричиной разнобоя. Фронты и бэки, после жуткого срача и попойки с дракой (да, у нас настоящий стартап) - провели компитишн. И оказалось что snake_case тупо проще и скорее в наборе на ~20%. На нем и остановились.

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