Оживленная дискуссия под моей первой статьей (https://habr.com/ru/articles/940782/) показала: разговор о единстве языка со сферой программирования задевает многих за живое. Тем не менее, cпасибо всем за сотню комментариев, сохранений и невероятно полезного и ценного опыта!

Однако язык — это не просто словарь, а динамическая система, в которой слова живут, взаимодействуют и порождают смыслы, выходящие за пределы их словарных значений. Следующим логическим шагом, таким образом, становится переход от статики «слова» (имени) к динамике «высказывания» (кода в действии).

Вместе с тем один из наиболее сильных и частых аргументов от скептиков звучал примерно так: «весь код — это чистая, бездушная логика для машины». На мой взгляд, это самое большое заблуждение в этой индустрии. Знали ли вы, что оператор + в вашем коде семантически богаче, чем многие слова в русском языке? Или что конструкция if not my_list — это не просто синтаксис, а настоящая идиома, которая отделяет «носителя языка» от «иностранца»? Задача настоящей работы — исследовать, как в строго детерминированной среде кода возникают сложнейшие семантико-прагматические явления, свойственные живому языку. 

Давайте забудем про имена и заглянем в самое сердце кода — в его грамматику и риторику. Пристегните ремни безопасности :)

Часть 1. Семантический спектр знака-оператора + в программном коде: от сложения до перегрузки

В лингвистике есть понятие «полисемии» — это когда одно слово имеет несколько разных, но семантически связанных значений. Например, глагол «идти» может означать и движение ногами, и выпадение осадков («дождь идет»), и даже то, что одежда вам к лицу («костюм идет»). Всё это разные действия, но их объединяет общая идея «движения» или «процесса».

Теперь давайте обратимся к оператору + :

Обсуждаемая лексема демонстрирует широкий спектр связанных значений, объединенных семантическим инвариантом «соединение» или «комбинация». Её конкретное значение систематически варьируется в зависимости от типа данных операндов. На самом фундаментальном уровне оператор + реализует две наиболее частотные операции:

Пример 1. Арифметическое сложение (числовые типы)

result = 42 + 58  

В выражении 42 + 58 оператор реализует математическую операцию нахождения суммы. Концептуальная семантика здесь совпадает с общепринятой математической, а операциональный результатом является целочисленное значение 100.

Пример 2. Текстовая конкатенация (строки)

greeting = "Hello" + " " + "World" 

Однако применительно к операндам-строкам происходит семантический сдвиг и оператор выполняет функцию лингвистической когезии, соединяя дискретные фрагменты в единое целое. Операционально это приводит к созданию новой строки "Hello World".

На более высоком уровне абстракции + применяется к структурам данных, таким как списки и кортежи. 

Пример 3. Объединение последовательностей (операнды-списки)

combined_list = [1, 2, 3] + [4, 5, 6]  

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

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

Наиболее же ярко продуктивный характер семантики в коде проявляется в механизме перегрузки операторов. Это техническое средство позволяет программисту целенаправленно наделять существующий знак (+) новым, предметно-ориентированным значением для собственных типов данных, что является вершиной абстракции. Так, разработчик (автор кода) может сам определить поведение знака + для созданных им типов данных. Например, для класса объектов типа Vector операция + будет означать векторное сложение, для класса Matrix — сложение матриц, а для класса Document — слияние двух документов. Это показывает, что программист, подобно носителю языка, не только использует существующие значения, но и создает новые по аналогии, опираясь на инвариантный смысл «соединения», что служит прямым доказательством продуктивности языковой системы кода.

Функция len(): полиморфизм как источник полисемии в коде

Аналогичное явление демонстрирует функция len(). Единая лексическая единица (имя функции) с инвариантным значением «измерение величины/размера» инициирует различные, хотя и семантически связанные, вычислительные процедуры в зависимости от контекста — типа объекта. В терминах информатики данное явление носит название полиморфизм (букв. «многоформие») и известно как способность функции обрабатывать данные разных типов. Но если посмотреть на это с точки зрения филологии, то полиморфизм — это не просто технический трюк. Это механизм, который встраивает принцип многозначности, как в живом языке, в строгую систему кода, выступая тем самым ярким проявлением полисемии, когда за разными действиями скрывается единный концепт/значение.

Пример 4. Измерение длины строки

length = len("Python")

Концептуальная семантика (для человека) → определение количества символов в текстовой последовательности.

Операциональный результат (для машины)  → 6; (тип данных: integer).

Пример 5. Подсчет элементов в списке

count = len([1, 2, 3, 4, 5])

Концептуальная семантика → подсчет количества элементов в упорядоченной изменяемой коллекции.

Операциональный результат → 5; (тип данных: integer).

Пример 6. Подсчет пар в словаре

size = len({'a': 1, 'b': 2})

Концептуальная семантика → подсчет количества пар «ключ-значение» в ассоциативном массиве.

Операциональный результат → 2; (тип данных: integer).

В каждом случае функция len(…) демонстрирует полисемию, семантически адаптируясь к различным структурам данных — от линейных последовательностей до ассоциативных массивов. Аналогично оператору +, концепт «длины» является расширяемым: программист может наделить им любой созданный им пользовательский тип данных, определив, что именно будет считаться его «размером» — например, количество страниц для объекта «Книга» или число сотрудников для объекта «Отдел».

Вывод по части 1

Таким образом, вопреки представлению о семантической однозначности искусственных языков, в программномкоде обнаруживается многоаспектная и структурированная полисемия. Знаки вроде оператора + и функции len() перестают быть простыми командами и становятся семантическими центрами, аккумулирующими вокруг себя целые «гнезда» связанных значений. Это позволяет говорить не просто об аналогии, а о наличии в коде фундаментальных лингвистических механизмов, присущих развитым, живым знаковым системам. В этих механизмах заключается практическое разрешение диалектического противоречия между человеческой и машинной семантикой. 

Соответственно, cистемы перегрузки операторов (+),полиморфизма (len()) выступают семантическим «медиатором», позволяющим согласовывать гибкость и многоплановость человеческого мышления с формальной строгостью исполнительной среды. Они являются тем самым мостом, который обеспечивает функциональный изоморфизм между концептуальным планом кода (как он мыслится человеком) и его операциональным планом (как он исполняется машиной), будучи одним из важнейших продуктов эволюции профессиональной деятельности программирования. Принципиально важным для этого сопоставления является то, что, как и в случае с полисимией естественного языка, эти операциональные значения не произвольны, а семантически мотивированы: например, программист наделяет свой объект способностью к сложению именно потому, что это соответствует его концептуальной природе в предметной области.

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

  • Для машинной логики (интерпретатора или компилятора), которая не оперирует концептами, а лишь следует формальному протоколу диспетчеризации («найти и вызвать метод с определенным именем», не усматривая между ними внутренней концептуальной связи), эти операци являются совершенно разными командами, которые просто случайно обозначаются одним и тем же символом. В зависимости от типа данных процессор вызывает совершенно разные, не связанные для него внутренне функции, что является классическим примером омонимии, то есть совпадением формы-имени при полном отсутствии связи в содержании.

Парадоксальность ситуации заключается в том, что вся когнитивная и инженерная сложность программирования, по сути, концентрируется в задаче обеспечения строгого изоморфизма между этими онтологически разнородными планами. Поведение машины, оперирующей детерминированными инструкциями, должно в точности реализовывать тот замысел, что существует в концептуальном поле программиста. Нарушение этого изоморфизма приводит к тому, что программистам хорошо известно как «баг» (англ. bug). С филологической точки зрения, «баг» – это не синтаксическая некорректность, а семантическая ошибка: диссонанс между планом содержания (авторский замысел) и планом выражения (текст кода), и то, что программист намеревался выразить, онтологически расходится с тем, как это интерпретирует и исполняет машина. Для формального анализатора, будь то компилятор или языковая модель, эта ошибка не существует, так как код может быть синтаксически безупречен. В этом и кроется фундаментальное ограничение сугубо формальных подходов, поскольку это антропоцентрическое семантическое измерение, где значение рождается из авторского намерения, по своей природе принципиально недоступно для них. Современные большие языковые модели, как и статические анализаторы, архитектурно ограничены операциональным планом. Они способны с высокой точностью распознавать и имитировать синтаксические паттерны, но остаются невосприимчивы к его концептуальному ядру, то есть авторскому замыслу.

Часть 2. Идиоматичность (или почему ваш код выдает в вас «иностранца»)

Если полисемия с омонимией — это скрытая «грамматика» кода, то идиоматичность — это его риторика и стиль.

В любом естественном языке есть просто «правильные» фразы, а есть фразы, которые используют «носители языка». Например, иностранец скажет: «Я желаю тебе удачи на экзамене». На идиоматичном русском языке это могло бы звучать следующим образом: «Ни пуха ни пера!». Смысл один, но второй вариант безошибочно выдает «своего».

В программировании — абсолютно то же самое. Существует понятие «идиоматического кода», когда вы пишете не просто формально корректно, а так, как принято в сообществе вокруг конкретного языка, используя его «дух» и философию .

Игнорирование идиом — это верный способ продемонстрировать отсутствие чувства языка либо выдать в себе «иностранный акцент», который переносит привычки из другого языка (например, из C++ в Python), не понимая местной культуры.

Давайте посмотрим на классические примеры из Python. Вот таблица, где слева — код, который поймет любой компилятор, а справа — код, который поймет и одобрит любой опытный «питонист»:

Задача

Неидиоматический код (формально верный)

Идиоматический ("pythonic") код (предпочтительный)

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

if len(my_list) == 0:

if not my_list:

Итерация с получением индекса

for i in range(len(my_list)):
item = my_list[i]

for i, item in enumerate(my_list):

Проверка на None

if variable == None:

if variable is None:

Для машины здесь абсолютно нет никакой разницы. Но для человека-читателя она колоссальна. Использование идиомы if not my_list — это не просто экономия символов. Это метакоммуникативный акт, с помощью которого вы без слов сообщаете коллеге: «Я — „свой“. Я знаю, что в Python пустые коллекции трактуются как False. Я понимаю „дух“ этого языка». Другими словами, использование той или иной конструкции перестает быть чисто техническим решением и становится особым маркером, который «сообщает»/сигнализирует об уровне интеграции автора в профессиональное сообщество и его принадлежности к культуре «носителей» языка, демонстрируя владение не только его грамматикой, но и его прагматикой. Игнорирование идиом (range(len())) воспринимается не как ошибка, а как сигнал о том, что автор еще не до конца погрузился в культуру и философию инструмента, которым пользуется.

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

Заключение: от грамматики (знания языка) к «чувству языка»

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

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

  1. Вы перестаете просто заучивать синтаксис и начинаете чувствовать его.

  2. Вы смотрите на новый язык или фреймворк и интуитивно ищете в нем идиомы, чтобы говорить на нем как «носитель», а не как «иностранец».

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

Это и есть то самое «чувство языка», которое отличает ремесленника от мастера. А это значит, что самый важный инструмент программиста находится не в IDE, а у него в голове. И это — его язык.

Спасибо за то, что дочитали до конца!

Данная статья является прямым продолжением моих размышлений о связи человеческого языка и программного кода. 

Комментарии (18)


  1. artptr86
    27.08.2025 07:02

    код — это язык или формальная система

    Стоило бы уточнить, что именно здесь подразумевалось под термином «язык». Язык в общем случае может быть формальной системой, тогда он является формальным языком. Языки программирования являются формальными.


    1. art_lak Автор
      27.08.2025 07:02

      Спасибо за ваше замечание! Сейчас попробую объяснить свою мысль.

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

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


      1. artptr86
        27.08.2025 07:02

        Светофор является формальным языком со своим алфавитом и грамматикой, поэтому вам следовало бы везде, где вы упоминаете термин «язык», заменить его на более конкретный. Обычно филология не рассматривает формальные языки, поэтому такое уточнение не требуется, но вы же идёте в ту отрасль, где это разделение имеет значение.


        1. art_lak Автор
          27.08.2025 07:02

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

          Тем не менее, благодаря таким комментариям и получается увидеть то, на что раньше не обращал особого внимания. Спасибо большое!


  1. katyastorm
    27.08.2025 07:02

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

    Когда началось про носителей языка , я подумала , что будет : вот русскоязычные разработчики предпочитают , а китайские разработчики предпочитают .... Статья не об этом , но было бы интересно узнать влияет ли родной язык на применения второго языка - python


    1. art_lak Автор
      27.08.2025 07:02

      Ваша мысль про высокоуровневые языки абсолютна корректна и точна!

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


  1. vadimr
    27.08.2025 07:02

    Для человека код наделён семантикой (денотационной и операционной). Для компьютера код - это чистый синтаксис.


    1. art_lak Автор
      27.08.2025 07:02

      Спасибо за ваше уточнение! В своей работе я провожу следующее разграничение:

      для человека код действительно обладает богатой семантикой (то, что я назвал «концептуальным планом» или авторским замыслом). Мы видим за + общую идею «соединения», а за if not my_list — культурную идиому. Для компьютера же, это действительно чистый синтаксис, набор формальных правил для выполнения (что я определяю как «операциональный план»). Интерпретатор просто следует протоколу, не вникая в общий смысл.


      1. vadimr
        27.08.2025 07:02

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


        1. art_lak Автор
          27.08.2025 07:02

          Я непременно над этим задумаюсь, чтобы не обобщать (упрощать) такие вещи в дальнейшем! Огромное спасибо за то, что уделили свое время и объяснили мне это!


      1. artptr86
        27.08.2025 07:02

        Для полноты анализа стоило бы рассмотреть также языки, где нет перегрузки операторов (в этом случае операции выражаются функциями или методами класса). Кроме того, даже в случае наличия в языке перегрузки, её может не хватать.

        Например, для сложения векторов можно использовать привычный из математики оператор «+», но для них определено два вида произведения (скалярное, в математике обозначаемое точкой, и векторное, обозначаемое крестиком), при этом оба символа, как правило, нельзя использовать в качестве операторов. Поэтому или звёздочку умножения используют для скалярного произведения, а для векторного — иной символ с другой изначальной семантикой (например, «&», означающий побитовое «И»), или одну или обе операции всё равно обозначают словами для очевидности.


        1. art_lak Автор
          27.08.2025 07:02

          Да, спасибо!

          Я ещё думал рассмотреть различные случаи итерации с использованием ключевого слова for, демонстрирующие, как единый синтаксический паттерн реализует универсальную концепцию последовательного обхода для гетерогенных типов данных. Это как раз относится к числу наиболее ярких примеров того, как единый синтаксис обеспечивает семантически универсальное поведение.


          1. artptr86
            27.08.2025 07:02

            Единый синтаксис обеспечивает семантически универсальное поведение? Это как раз соответствует принципу наименьшего удивления.

            Намного интереснее (но и сложнее) рассмотреть случаи, когда синтаксически одинаковый код имеет различную семантику в зависимости от контекста. Таким поведением обладает язык C++. Например, даже в простейшем случае:

            A * B;

            Если A — это переменная, то данное выражение является вызовом оператора умножения для переменных A и B. Если же A — это тип, то данное выражение является объявлением переменной B с типом A *.

            Это даже если не касаться шаблонов и правил их разрешения.


            1. art_lak Автор
              27.08.2025 07:02

              Большое спасибо за такую замечательную подсказку!!!


  1. EchoA
    27.08.2025 07:02

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


    1. art_lak Автор
      27.08.2025 07:02

      Интересно, обязательно об этом сейчас посмотрю! Спасибо за информацию!


  1. kmatveev
    27.08.2025 07:02

    Нейросетка продолжает словоблудить. Какая к чёрту "полисемия" в функции len(), она делает ровно одну вещь во всех случаях, это одна семантика.


    1. art_lak Автор
      27.08.2025 07:02

      Смотрите, я уже не раз уточнял, что моя главная цель — совершить попытки найти глубинную связь естественного языка и современного кода, а без примеров с полисемией это было бы совершенно несостоятельно в методологическом плане. Это не идеи нейросети, а я последовательно двигаюсь от одного уровня к другому, принимая во внимание все важнейшие лингвистические механизмы.