Многим разработчикам может казаться, что эволюция языков программирования застопорилась: на рынке то и дело появляются новые синтаксические обёртки для уже известных концепций. Тем не менее, время от времени всплывают идеи, которые заставляют нас задуматься — а что, если мы подумаем о «функциях», «динамических типах» и «реляционных структурах данных» иначе?
Ниже — мой взгляд на несколько таких идей (вдохновлённых заметкой Some Programming Language Ideas), которые могут показаться причудливыми, но, возможно, посеют зерно новых решений.
✨ Ослабленные функции (Loosen Up The Functions)
Стандартный вызов функции — очень жёсткая конструкция. Мы привыкли, что strlen() точно доступна, что её вызов гарантированно «не провиснет» на минуту, а в худшем случае выбросит исключение. Но что, если мы взглянем на функции так же, как на удалённые RPC-вызовы?
- 🤔 Новые «ошибки»
• Функция может внезапно стать «недоступной» (аналогично сети, которая обрывается).
• Вызов может «тайм-аутиться» или «зависнуть» на неопределённый срок.
В таком сценарии каждая функция в языке получает «асинхронные» и «обрывающиеся» характеристики по умолчанию. Тогда и распределённые вызовы (RPC) становятся более естественными — мы уже не ждём от функций жёсткой безошибочности. Но тут же встаёт вопрос: как вернуть простоту «обычной» функции, когда никаких сетевых отказов и тайм-аутов нет? Возможно, мы дадим синтаксису специальную подсказку «это локальный вызов, работать будем без лишних проверок». Компилятор (или рантайм) может автоматически оптимизировать «местные» вызовы до обычного «invoke».
Плюсы: Единая модель для локальных и распределённых функций.
Минусы: Придётся усложнять систему ошибок и тайм-аутов, что может отпугнуть многих программистов.
✨ Возможности (Capabilities)
Идея «capabilities» не новая (язык E пытался это реализовать), но время могло созреть. Суть: каждая сущность (объект, функция) в системе имеет конкретные «способности». Например, право читать файловую систему, доступ к сети, управление памятью и т. д. Если язык сразу строится на принципе capability, то:
- 🤝 Безопасность и контроль
Код не может бесконтрольно творить всё что угодно: он может лишь то, на что у него есть способность. - ⚙️ Встраивание подобных моделей
Уход от «глобальных» API к точечно выдаваемым «ключам» (capabilities), которые определяют, что именно можно делать.
Многие системы пытаются «дополнительно» прикручивать управление доступом, но полноценная реализация в самом языке может дать более надёжное решение.
✨ Продакшн «из коробки» (Production-Level Releases)
В последние годы мы разобрались, что для стабильного продакшена нужны общие подходы к логированию, метрикам, трейсингу и т. д. Но почти все языки предоставляют это как «плагины» или внешние библиотеки.
Почему бы не оснастить язык такими вещами на фундаментальном уровне?
- 🏭 Единый интерфейс для логирования
Нет необходимости выбирать из 10 вариантов — есть официальная поддержка, понятная всем. - ⚙️ Метрики и трейсинг
Представьте встроенный механизм «контекста запроса» (request context), чтобы любые библиотеки могли без боли регистрировать нужные метрики и связывать логи.
Риск: когда такие «благоразумные» части вшиты в ядро, их эволюция затрудняется. Но выигрыш — экосистема библиотек получает единый стандарт, без «зоопарка» и конфликтов.
✨ Полудинамический язык (Semi-Dynamic Language)
Динамические языки удобны (Python, Ruby, JavaScript), но часто страдают от медлительности. Супероптимизация всё ещё не может приблизить их к C. Зато разработчики замечают, что большая часть «динамики» нужна во время инициализации (подгрузка плагинов, формирование классов), а в рутине — нет. Как насчёт механизма «локдауна»?
- В фазе старта код ведёт себя как динамический: можно модифицировать классы, добавлять методы.
- Затем говорим: «Всё, теперь фиксируем структуру, становимся практически статически типизированным».
- Компилятор (JIT) в этот момент получает полную картину и может генерировать быстрый код.
Это не радикально новая концепция, но целенаправленный язык, поддерживающий такой «замороженный» режим, мог бы дать выигрыш в скорости и гибкости. Возможно, мы бы пришли к чему-то вроде LuaJIT, но без необходимости урезать язык.
✨ Value Database (идея «храним всё прямо в языке»)
Некоторые среды (Smalltalk, Frontier) позволяли делать global.x = 1, и значение сохранялось «навсегда» даже после закрытия приложения. Это звучит соблазнительно, но легко приводит к хаосу: «волшебные» значения остаются в «глобальном» состоянии и ломают продакшен, если кто-то случайно изменил их на тестовом сервере.
Тем не менее, автор задаётся вопросом: можно ли это оживить «правильными» типами, механизмом событий, гибкими доступами? Когда всё «просто работает» без сериализации, JSON и SQL, — это чертовски удобно. Но потребуется мощный контроль (например, структурная типизация, ограничения на изменение). Пока такие идеи редко встречаются, потому что слишком велик риск «энтропии».
✨ Настоящий реляционный язык (A Truly Relational Language)
Почему бы не сделать основной структурой данных отношения (таблицы)? Современные языки:
- 👀 считают, что всё — это либо объекты, либо структуры,
- 💾 подключают SQL через ORM или «дикие» запросы,
- 🤷♂️ преобразуют реляционную модель в объекты (часто с болью).
А что, если язык изначально умеет работать с наборами строк, соединять таблицы, а типы результатирующих «join’ов» становятся новыми сущностями? Без текстового SQL, но с мощной реляционной семантикой.
- 👩💻 LINQ-подобные фичи показывают, что это реально. Но если всю грамматику языка «заточить» под это, появятся уникальные возможности.
- 🏷️ Row types и возможность создавать «методы» для «на лету» сформированных наборов столбцов? То есть мы делаем Join нескольких наборов данных и сразу пишем методы для результата. В классических языках это делается костыльно.
Это может быть очень интересно, хотя и нарушает привычный «объектный» или «функциональный» подход.
✨ Язык для модульных монолитов
«Модульный монолит» (modular monolith) стал популярной архитектурой:
- один код-база, но разбитая на чёткие модули с «интерфейсами» вместо хаотичного монолита,
- сохраняет целостность (в отличие от микросервисов),
- гибкость достигается Dependency Injection и чётко прописанными границами.
Но большинство языков мешают созданию удобного модульного монолита:
- В статических языках нужно объявлять кучу интерфейсов, что иногда кажется «бюрократией» и приводит к тому, что люди просто «хардкодят».
- В динамических всё проще, но нет compile-time гарантий.
Идея: сделать язык, где параметр функции автоматически трактуется как интерфейс того, что мы с ним делаем. Если мы применяем к параметру «сложение строк» и «итерацию по символам», то сам язык выводит «ОК, это тип Stringish, умеющий + и итерацию». При этом сохраняется статическая проверка.
Добавьте сюда динамические области видимости (dynamic scopes), в которых можно подменять сервисы (например, файл-систему на mock при тестировании), и вы получите гибкую почву для «настоящего модульного монолита»: всё, что вам нужно, — это переключить одну «глобальную» ссылку на «другой» объект, а код будет использовать новый ресурс.
✨ Модульный линтинг (Modular Linting)
Язык Go славится golangci-lint: набор отдельных линтеров, объединённых в одном фреймворке. Но представьте, если в языке изначально:
- 🤖 Продумана AST-архитектура для сторонних линтеров,
- ⚙️ Есть лёгкий способ «вписывать» их (через standard interface),
- 🏆 Много «обязательных» правил (как у Go — неиспользуемые переменные, пустые импорты и т. д.) можно вынести в линтеры, а не жёстко прописывать в компиляторе.
Тогда язык может быстро расти, не становясь заложником «бесчисленных фич» в ядре. Сообщество само создаст плагины, которые проверяют разные аспекты кода, в том числе специфичные для отдельных библиотек (например, шаблоны HTML или RPC-вызывов). Минус — появляются «диалекты» в зависимости от настроек линтера, но это и так происходит в крупных проектах.
Заключение
Новаторские идеи не обязаны быть идеальными или универсальными. Часть из них может оказаться «провальными», часть — объединится в нечто более жизнеспособное, а отдельные «сумасшедшие» тезисы дадут толчок к новым решениям. И это нормально: язык программирования — это не только синтаксис и компилятор, но и целая парадигма, задающая, как мы думаем о коде.
Иногда небольшие «сдвиги» — вроде повышения асинхронности, внедрения реляционных структур или осмысленной интеграции продакшн-инструментов — могут стать тем самым скачком в эволюции, который откроет дорогу для более серьёзных перемен.
Ссылки на оригинальную новость и источники: