Добавить в корзинуПозвонить
Найти в Дзене
Цифровая Переплавка

Новые идеи в языках программирования: расширяя горизонты и выходя за рамки привычного

Многим разработчикам может казаться, что эволюция языков программирования застопорилась: на рынке то и дело появляются новые синтаксические обёртки для уже известных концепций. Тем не менее, время от времени всплывают идеи, которые заставляют нас задуматься — а что, если мы подумаем о «функциях», «динамических типах» и «реляционных структурах данных» иначе? Ниже — мой взгляд на несколько таких идей (вдохновлённых заметкой Some Programming Language Ideas), которые могут показаться причудливыми, но, возможно, посеют зерно новых решений. Стандартный вызов функции — очень жёсткая конструкция. Мы привыкли, что strlen() точно доступна, что её вызов гарантированно «не провиснет» на минуту, а в худшем случае выбросит исключение. Но что, если мы взглянем на функции так же, как на удалённые RPC-вызовы? В таком сценарии каждая функция в языке получает «асинхронные» и «обрывающиеся» характеристики по умолчанию. Тогда и распределённые вызовы (RPC) становятся более естественными — мы уже не ждём от
Оглавление

Многим разработчикам может казаться, что эволюция языков программирования застопорилась: на рынке то и дело появляются новые синтаксические обёртки для уже известных концепций. Тем не менее, время от времени всплывают идеи, которые заставляют нас задуматься — а что, если мы подумаем о «функциях», «динамических типах» и «реляционных структурах данных» иначе?

Ниже — мой взгляд на несколько таких идей (вдохновлённых заметкой 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. Зато разработчики замечают, что большая часть «динамики» нужна во время инициализации (подгрузка плагинов, формирование классов), а в рутине — нет. Как насчёт механизма «локдауна»?

  1. В фазе старта код ведёт себя как динамический: можно модифицировать классы, добавлять методы.
  2. Затем говорим: «Всё, теперь фиксируем структуру, становимся практически статически типизированным».
  3. Компилятор (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-вызывов). Минус — появляются «диалекты» в зависимости от настроек линтера, но это и так происходит в крупных проектах.

Заключение

Новаторские идеи не обязаны быть идеальными или универсальными. Часть из них может оказаться «провальными», часть — объединится в нечто более жизнеспособное, а отдельные «сумасшедшие» тезисы дадут толчок к новым решениям. И это нормально: язык программирования — это не только синтаксис и компилятор, но и целая парадигма, задающая, как мы думаем о коде.

Иногда небольшие «сдвиги» — вроде повышения асинхронности, внедрения реляционных структур или осмысленной интеграции продакшн-инструментов — могут стать тем самым скачком в эволюции, который откроет дорогу для более серьёзных перемен.

Ссылки на оригинальную новость и источники: