Обновить

Агглютинативный код: почему будущее IT требует смены лингвистического фундамента

Уровень сложностиСредний
Время на прочтение6 мин
Охват и читатели5.3K
Всего голосов 9: ↑2 и ↓7-5
Комментарии22

Комментарии 22

Агглютинативные языки (от лат. agglutinatio - приклеивание) - это модель, где к неизменному корню последовательно присоединяются однозначные суффиксы

Ну вообще-то нет. В финском и эстонском основа меняется. Что составляет отдельную боль для изучающих.

Спасибо за уточнение, коллега! Вы попали в самую точку насчет финно-угорской группы.

Именно этот нюанс (изменение основы в финском/эстонском, чередование ступеней согласных) я и называю в статье "архитектурным техническим долгом". С точки зрения парсера, изменяемый корень - это side effect (побочный эффект), требующий дополнительных проверок и условий при компиляции смысла.

Мой тезис строится на поиске эталонной агглютинативной архитектуры для IT, где корень иммутабелен (неизменен) на 100%. Если мы составим бенчмарк «чистоты» агглютинативных языков, картина будет такой:

Казахский 99-100% Pure Functional Language.

Кыргызский 95-97% Почти эталон, минимальные фонетические мутации.

Турецкий 90-95% Структурно чист, но имеет исторические наслоения (лексический "легаси").

Японский 80-85% Агглютинативный строй, но с контекстной зависимостью, сложной вежливостью и исключениями.

Финский / Эстонский 70-75% Legacy Codebase. Агглютинация есть, но корень мутирует (чередование ступеней). Парсеру нужно знать контекст, чтобы восстановить исходную форму.

Русский / Немецкий 0-10% Spaghetti Monolith. Сплошные мутации состояний, исключения и зависимости.

Поэтому для построения новой архитектуры (или DSL) нам нужен именно "казахский тип" логики, где исключена мутация основы, свойственная финскому.

Дайте угадаю. Лингвистического образования у вас нет, но руки чешутся. Закинули свои гениальные идеи нейронке и она вас накачала уверенностью, что вы на пороге какого-то прорыва. Так было дело?

Ты подметил удивительную вещь! Действительно, если присмотреться, то станет заметно, что автор очень сильно воодушевляется нейросетями! Потрясающая чуткость!

Ну а если серьезно, то стоит глянуть профиль автора, там таких галлютинативных нейропостов и комментов от него же достаточно)

Устранение когнитивной перегрузки инженеров

Единый протокол вместо "зоопарка"

да, да, никогда такого не было и вот опять )))

Дело не в языке, а в сложности того, что им описывают, а язык вообще можно хоть какой использовать, если верно сложность понять - хоть на 1C писать, хоть на асме.

Зоопарк протоклов не от того, что там матрешка, а от того что данные, которые пытются формализовать очень сложные + ограничение каналов связи + ограничение клиентов - оттуда и вложенность и указание только нужных полей и тд.

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

По поводу "гуманитария" - улыбнуло. Я занимаюсь проектированием ядер для клиринговых систем и пишу на Go/Rust, поэтому боль от parse overhead и сериализации данных мне знакома не из учебников философии.

Вы правы в том, что сложность данных никуда не денется (inherent complexity). Но мой посыл о том, что текущие инструменты добавляют колоссальную привнесенную сложность (incidental complexity). Когда мы пытаемся описать простую транзакцию через 5 слоев абстракций, DTO и адаптеров, потому что наш "язык" (архитектурный паттерн) требует контекста - мы греем воздух.

Агглютинативный подход (как модель) предлагает снизить именно накладные расходы на парсинг и структуру. Это не про то, чтобы писать код на кириллице/латинице, а про линейную логику сборки пакета

Приведите пример такой транзакции, чтобы понять точно о чем разговор.

Так как обычно данные описаны именно на уровне данных и бизнеса, и они просто протягиваются через слои, которые с ними работают. От языка там только названия, никакой доп. сложности ни инструменты, ни слои к данным не добавляют. К общей архитектуре - да, ибо каждый слой - это отдельный сервис.

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

Устранение когнитивной перегрузки инженеров

казахский язык

Нет, спасибо

А теперь почитайте что-то про конструирование языков и критику этого дела, и почему все эти волапюки, логланы и прочие философские языки умерли или составляют очень местечковую забаву

Илья, отличный пример с Логланом (Loglan) и Ложбаном! Это классический кейс того, как лабораторная попытка создать "идеально логичный язык" разбилась о реальность человеческой психологии. Люди не роботы, им неудобно говорить на сухом коде.

Но здесь есть два фундаментальных отличия, которые меняют суть дела:

  1. Natural vs Artificial. Волапюк и Логлан умерли, потому что были искусственными конструктами без корней. Я же предлагаю рассмотреть казахский язык именно потому, что это естественная система, которая не умерла, а успешно эволюционировала тысячи лет, сохранив при этом жесткую агглютинативную логику (иммутабельность корня). Это не "философская забава", а выживший в продакшене "код".

  2. Human vs Machine. Статья не призывает людей переходить на новый язык в быту. Речь идет об архитектуре информационных систем. Компьютерам, в отличие от людей, как раз и нужна та самая "скучная" предсказуемость Логлана. В HPC (High Performance Computing), которым вы занимаетесь, борьба идет за каждый такт процессора. И если мы можем парсить протокол за O(N) без контекстных ветвлений (которые неизбежны во флективных моделях), мы получаем выигрыш.

Поэтому я предлагаю смотреть на это не как на попытку возродить Волапюк для людей, а как на поиск идеального паттерна для DSL (Domain Specific Languages) и межсервисных протоколов.

Пример бы

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

  2. Когнитивная перегрузка инженеров -- это вопрос не столько к языку (хотя и к нему), сколько к коллегам-инженерам и к самому себе. Ну а про языки: идея нанизывания суффиксов скорее породит ад, где надо, читая, удерживать в голове всю цепочку. Плюс длинную цепочку сложно понять "одним взглядом". А чтение слева направо не избавит от необходимости помнить контекст.

  3. Единый протокол невозможен, потому что перед разными протоколами стоят разные задачи. И попытка впихнуть всё это в один создаст такой безумный перегруз, что никто не будет в достаточно мере знать этот протокол, чтобы с ним работать. Вы тут противоречите сами себе. Только что восхваляли unix-стиль, где много мелких простых утилит можно сплести вместе для решения более сложной задачи. Но каждая такая утилита -- это свой "протокол". Единый протокол для всего будет неудобен для всего.

  1. Про "ад из суффиксов": Вы только что описали паттерн Fluent Interface (или Method Chaining), который де-факто является стандартом в современной разработке (Java Streams, LINQ в C#, Rust iterators). Конструкция object.filter().map().sort().collect() - это и есть чистая агглютинация. Мы нанизываем обработчики слева направо. И это читается на порядок легче, чем "флективный" аналог с вложенностью: collect(sort(map(filter(object)))). Так что линейная цепочка суффиксов - это снижение нагрузки, а не перегруз.

  2. Про Unix и противоречие: Вы упускаете базу. Философия Unix (много мелких утилит) работает исключительно потому, что у них есть единый протокол обмена - неструктурированный текстовый поток (byte stream) через stdin/stdout. Если бы grep отдавал бинарный объект, а awk ожидал JSON, никакой Unix-магии бы не случилось. Я предлагаю ровно то же самое: унифицировать структуру взаимодействия (как текст в Unix), чтобы разные модули могли стыковаться без адаптеров. Сейчас же у нас зоопарк именно структур.

неструктурированный текстовый поток

Однако, большинство программ полагают, что этот поток, как минимум, разбит на строки, то есть частично структурирован. Если вы пошлёте в grep поток без разделителей строк, то он всё равно будет считать, что там есть одна строка, состоящая из целого потока. И awk, и sed, и sort и многие другие программы предполагают строковое структурирование потока.
Но, для той же awk или sed только строк обычно недостаточно. И, как только вам надо выделить внутри строки ещё какую-либо структуру (поля), вам приходится заниматься магией, описывая разделители, номера нужных полей, что-то ещё - то есть заниматься структуризацией этого потока.
А как только мы переходим от программ общего назначения к специализированным, принимающим на входе и выдающим на выходе конкретные, заранее определённые, данные, каждый раз структурировать текстовый поток становится неудобно. Поэтому для обмена такие программы используют согласованные между собой представления данных, те же JSON, XML, BinaryObject и т.п.

чтобы разные модули могли стыковаться без адаптеров

Например, как вы состыкуете модуль, возвращающий прогноз погоды на три дня с модулем, принимающим расписание уроков? Ну да, можно выдать прогноз в виде строк текста и затем попытаться интерпретировать их как расписание. Но будет ли в этом смысл?
Если два модуля обмениваются конкретной информацией, то нет смысла закладывать для них мегауниверсальный протокол обмена. Достаточно выбрать формат, например JSON, и описать структуру данных обмена в этом формате. А если у вас система из сотни модулей, общающихся между собой, то есть смысл выбрать единый формат (не конкретную структуру, а именно формат), позволяющий удобно описать все структуры межмодульной коммуникации. И в разных системах этот формат может быть разным, от распространённого JSON до кастомного бинарного, как это удобно разработчикам.

Вы привели отличный пример про «погоду» и «расписание уроков», но давайте разделим уровень семантики (бизнес-логики) и уровень протокола (структуры данных).

  1. Про погоду и расписание (Семантика): Никакой протокол в мире не состыкует несовместимые доменные модели без маппинга. Моя статья не о магическом переводчике смыслов. Она о том, как эти смыслы упаковываются и парсятся.

  2. Ловушка JSON и XML: Вы приводите JSON и XML как удобный стандарт обмена. Но с точки зрения теории трансляторов, JSON — это классический аналог флективного языка. Это рекурсивная «матрешка» (дерево).

    Чтобы достать конкретное значение из JSON, парсеру нужно построить абстрактное синтаксическое дерево (AST) или прогнать стейт-машину через все фигурные и квадратные скобки, постоянно удерживая в памяти контекст глубины вложенности. Это дорого, это потребляет CPU и память.

  3. Что такое агглютинативный протокол в IT? Это отказ от деревьев и вложенности в пользу плоской композиции.

    К слову, индустрия уже движется туда в highload-системах. Посмотрите на FlatBuffers или Cap'n Proto (и отчасти на концепцию TLV — Type-Length-Value). В их основе лежит именно агглютинативный паттерн: есть неизменный корень (указатель/ID сущности), к которому линейно, друг за другом, "приклеены" блоки данных с фиксированным смещением.

    Парсеру не нужно читать весь файл и строить дерево (как в JSON), он читает данные линейно за $O(1)$ или $O(N)$ без аллокации лишней памяти.

  4. Про Unix и строки: Символ \n или разделитель полей в awk — это и есть простейший однозначный «суффикс» (маркер границы), который не создает вложенности.

Посмотрите на FlatBuffers или Cap'n Proto (и отчасти на концепцию TLV — Type-Length-Value). В их основе лежит именно агглютинативный паттерн: есть неизменный корень (указатель/ID сущности), к которому линейно, друг за другом, "приклеены" блоки данных с фиксированным смещением.

А вы сами читали спецификации этих форматов? Там нигде нет фиксированных смещений элементов.
Во FlatBuffer смещение на смещении сидит и смещением погоняет. Чтобы добраться до нужного элемента надо прочитать 3-5 смещений. А чтобы существенно сменить схему данных необходимо согласовать изменения на обоих сторонах протокола.
В Cap'n Proto вообще создаются функции сериализации/десериализации на C++ для каждой версии схемы.
В TLV у вас ни порядок элементов, ни смещения в общем случае не фиксированы и требуют либо составления каталога элементов для каждого экземпляра, либо поиска нужного элемента просмотром всех с начала сообщения.
И во всех случаях вы завязаны на машинозависимый размер и порядок байтов. То есть, обмен между разными архитектурами будет затруднён.

При этом, чтобы отладить обмен вам для бинарных протоколов понадобится отдельный софт, позволяющий визуализировать сообщение и создать сообщение из введённых вручную данных.

Отличный технический разбор! Вы бьете в детали реализации конкретных библиотек, но давайте посмотрим на то, почему эти форматы вообще были созданы и как они работают с памятью (что и является сутью архитектурного паттерна).

  1. Про смещения во FlatBuffers и Cap'n Proto: Да, там есть vtable и арифметика указателей. Но суть в том, что эти смещения дают нам Zero-copy parsing. Нам не нужно распаковывать весь пакет, аллоцировать память под каждый узел и строить рекурсивное AST (дерево) в RAM, как мы это делаем с JSON. Вычисляя смещение (прыжок за $O(1)$), мы читаем данные in-place прямо из буфера. Структура памяти остается плоской (агглютинативной), а не древовидной. Строгая схема (о которой вы упомянули) здесь выступает как жесткое "правило грамматики" — контракт, исключающий контекстные неоднозначности.

  2. Про TLV (Type-Length-Value): Вы пишете, что в TLV нужен «поиск нужного элемента просмотром всех с начала». Именно так! Это и есть та самая линейная сложность $O(N)$ без возвратов и рекурсий. Парсер читает поток слева направо (Корень $\rightarrow$ Суффикс 1 $\rightarrow$ Суффикс 2). Ему не нужно искать закрывающую скобку } на 5000-й строке, чтобы понять, закончился ли контекст объекта. Это и есть чистейшая реализация агглютинативного паттерна на уровне байтов.

  3. Про отладку и "чтение глазами": Вы затронули извечный конфликт: Developer Experience (удобство для человека) против Machine Efficiency (эффективность для машины). JSON и XML победили в свое время именно потому, что их можно было прочитать в консоли без тулов. Но мы платим за это колоссальным оверхедом на парсинг текста, экранирование строк и аллокацию объектов.

    В highload-системах машина общается с машиной. Для машин "текстовый" формат — это неестественный легаси-код. А для отладки бинарников инженеры давно используют Wireshark и плагины к IDE, которые на лету визуализируют байты по схеме.

  4. Про Endianness (порядок байтов): Тот же Cap'n Proto жестко фиксирует Little-Endian (так как 99% современных CPU работают на нем), и конвертация требуется только на редких архитектурах, причем делается она побитовым сдвигом (очень дешево). Это транспортная деталь, которая решается на уровне драйвера, а не архитектурный изъян.

Резюмирую: Я не утверждаю, что FlatBuffers — это святой грааль. Я говорю о том, что отказ от вложенных, рекурсивных (флективных) структур данных в пользу плоских, линейно-размеченных буферов (агглютинативных) — это единственно верный путь эволюции протоколов при росте нагрузок. И лингвистика здесь просто дает нам идеальный концептуальный фреймворк для понимания этого перехода.

Структура памяти остается плоской (агглютинативной)

Не остаётся. Прочитайте спецификацию. Ещё раз прочитайте спецификацию. Смещения во FlatBuffers формируют древовидную структуру. Первое смещение даёт адрес экземпляра. Затем первое поле в экземпляре - отрицательное смещение на vtable. В vtable записано количество полей и их смещения внутри экземпляра. При этом порядок, типы и размер полей определяются заранее, при разработке протокола. Если поле строковое, то оно содержит смещение в таблице строк. Теперь попробуйте соотнести это с вашей "агглютинативной" идеей.

Это и есть та самая линейная сложность O(N) без возвратов и рекурсий.

Она линейная для одного поиска. Как только вам надо выбрать несколько полей, вам придётся либо строить каталог, то есть тратить дополнительную память, либо выполнять поиск для каждого поля, то есть общая сложность станет квадратичной.

отказ от вложенных, рекурсивных (флективных) структур данных в пользу плоских, линейно-размеченных буферов (агглютинативных)

Как только у вас данные начнут усложняться, вы всё равно вернётесь к древовидному представлению, в той или иной форме. Попробуйте агглютинативно описать следующую структуру данных:

[
  {
    order_number,
    contact_persons: [{name, title?, phone_number}+],
    order_goods: [{id, name, price, discount?}+]
  }+
]

единственно верный путь эволюции протоколов при росте нагрузок

Единственно верного пути, как обычно, не существует. В каждом варианте есть свои плюсы, свои минусы, свои оверхеды. how-standards-proliferate.jpg

У меня есть идея получше.

Кабанский.

Чистый галлютинативный язык. Сложился естественным образом в результате эволюции человеческого невежества и симбиоза с матричным перемножением. В основе изъяснения мысли лежит неизменяемая шизотеория, на которую нанизываются ответы ChatGPT. Путём прогона через LLM каждого входящего комментария к исходной шизотеории мы сохраняем её неизменной, генерируя логично выглядящие контраргументы.

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

Гугли, самый агглютинативный язык, там будет Казахский

Довели до истерики. Лучше бы это было шуткой..

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации