Язык программирования

ИИ-обзор статьи
Язык программирования — формальный язык, предназначенный для записи компьютерных программ и определяющий набор лексических, синтаксических и семантических правил для управления работой ЭВМ.
От механических устройств к первым языкам высокого уровня
Прообразы языков появились ещё в XIX веке (Жаккардов станок, машина Бэббиджа). В 1930-1940-х годах были созданы абстракции алгоритмов (лямбда-исчисление, машина Тьюринга) и первый высокоуровневый язык Plankalkül. В 1950-х машинный код сменили ассемблеры, а с середины десятилетия — Фортран, Лисп и Кобол, универсальные языки третьего поколения.
Формальная грамматика и синтаксический анализ
Синтаксис языка задаётся грамматикой в форме Бэкуса-Наура. На её основе строится синтаксический анализатор (LL, LALR), создающий дерево разбора или AST.
Безопасность и системы типов
Безопасные языки предотвращают недопустимое поведение. Системы типов делятся на динамические (Lisp, Smalltalk) и статические (Algol, ML). Си и C++ небезопасны из-за частого приведения типов. Термин популяризировал Робин Милнер.
Автоматическая оптимизация благодаря ссылочной прозрачности
Ссылочная прозрачность позволяет без изменения поведения подставлять выражения, что открывает автоматические преобразования: устранение промежуточных данных, редукцию цепочек вычислений, мемоизацию. Пример — оптимизация хвостовых вызовов, гарантируемая в Scheme, но не всегда в Си.
Популярность языков и лидерство Python
Популярность языка трудно определить из-за контекста; основные метрики — число вакансий, книг, строк кода и поисковых запросов. По индексу TIOBE с 2020-х лидирует Python, достигнув 26,98% в июле 2025 года и сохраняя значительный отрыв.

Язы́к программи́рования — формальный язык для записи компьютерных программ[1]. Он задаёт лексические, синтаксические и семантические правила, определяющие вид программы и действия исполнителя (обычно ЭВМ). За историю вычислительной техники создано свыше восьми тысяч языков, включая эзотерические, визуальные и игрушечные, и их число растёт. В отличие от естественных языков, язык программирования предназначен для управления ЭВМ и содержит конструкции для определения структур данных и управления вычислениями. Он определяется как спецификацией стандарта, так и реализациями — средствами трансляции или интерпретации.

Общие сведения
Язык программирования
Область использования Разработка программного обеспечения
Дата появления 1940-е годы

История

Ранние этапы развития

Прообразы языков появились до электронных ЭВМ: уже в XIX веке программируемые устройства (музыкальные шкатулки, Жаккардовый ткацкий станок, 1804) управлялись наборами инструкций, а в 1843 году Ада Лавлейс написала для машины Бэббиджа программу вычисления чисел Бернулли. В 19301940-х годах А. Чёрч, А. Тьюринг и А. Марков предложили абстракции для формализации алгоритмов — лямбда-исчисление, машину Тьюринга и нормальные алгоритмы; тогда же (1943—1945) К. Цузе создал «Plankalkül», считающийся первым высокоуровневым языком.

В начале 1950-х программисты UNIVAC и IBM 701 работали в машинном коде (первое поколение). Первым практически реализованным языком стал в 1949 году «Краткий код», работавший через интерпретатор. Им на смену пришли языки второго поколения — ассемблеры и автокоды с мнемониками вместо адресов, но всё ещё привязанные к архитектуре.

С середины 1950-х появились языки третьего поколения — Фортран, Лисп, Кобол — и в конце десятилетия Алгол. Эти «языки высокого уровня» универсальны, не зависят жёстко от системы команд и исполняются на любой ЭВМ с транслятором; их обновлённые версии используются доныне.

Совершенствование

В 19601970-х годах сложились основные парадигмы — во многом как развитие языков третьего поколения:

Каждый из них породил семейство потомков. Тогда же велись споры о структурном программировании: Э. Дейкстра предлагал отказаться от GOTO.

Объединение и развитие

1980-е годы стали временем консолидации: C++ объединил объектно-ориентированное и системное программирование, США стандартизировали производный от Паскаля язык Ада, а в Японии исследовали языки пятого поколения. Заметной тенденцией стало сосредоточение на модулях (Модула-2, Оберон, Ада, ML), нередко с обобщённым программированием, а также визуальные языки, получившие в 2010—2020-е годы импульс в образовании и low-code-разработке.

В 1990-х с развитием Интернета распространились языки веб-сценариев (Perl, Java) и виртуализация. В 20002020-х годах вошли в широкое употребление C#, Python, JavaScript, Go, Rust, Kotlin и Swift, а развитие сместилось к безопасности, параллелизму, модульности и интеграции с базами данных.

Спецификация языков

Стандартизация

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

Алфавит

Современные языки (C#, Java, Rust, Delphi) используют символы Юникода; исторически, до начала 2000-х, применялась 7-битная ASCII. Использование символов вне 7-битного ASCII зависит от реализации. В СССР и России существуют языки с русскоязычными ключевыми словами — встроенный язык программирования 1С:Предприятие, школьный алгоритмический язык системы КуМир и РАПИРА.

Грамматика

Синтаксис языка обычно задаётся формальной грамматикой, чаще всего близкой к форме Бэкуса — Наура[2]. Большинство языков описываются контекстно-свободными грамматиками, лексемы — регулярными языками, а отдельные ограничения относят к контекстно-зависимым[3]. На основе грамматики строится синтаксический анализатор (методы LL(n), LALR(1)[2]), результатом чего становится Дерево разбора или AST[2].

Семантика

Семантика определяет смысл синтаксически корректных конструкций; её описывают тремя подходами[4]:

  • операционный описывает исполнение как шаги абстрактной машины;
  • аксиоматический — через логические последствия с пред- и постусловиями;
  • денотационный — сопоставлением конструкциям математических объектов.

Классификация

Общепринятой таксономии языков нет: одни признаки строго разделяют языки по техническим свойствам, другие условны, третьи субъективны. Как правило, язык имеет несколько предков — иногда с проверкой на непротиворечивость (см. Определение Standard ML), а иногда из практических потребностей без математической строгости, включая взаимоисключающие идеи (как C++).

Языки низкого и высокого уровня

«Уровень языка» означает степень отличия его семантики от машинного кода целевой архитектуры: язык тем «ниже», чем «ближе к машине». Критерии не всегда совпадают: «высокоуровневые» языки иногда реализуют аппаратно (Лисп-машина), а «низкоуровневый» ассемблер компилируют как «высокоуровневый» (VAX Macro на DEC Alpha), поэтому понятие условно.

К языкам низкого уровня относят машинные языки и ассемблеры: они дают точный контроль и быстродействие, но требуют переписывания при переносе. К 1970-м годам ответом на рост сложности стали языки высокого уровня, легче модифицируемые и переносимые. Наибольшее распространение получили языки третьего поколения, соответствующие машине фон Неймана; к четвёртому относят языки высшего порядка, а пятое (англ. very high level language) — логические языки, визуальные языки и языки на подмножестве естественного. К «языкам среднего уровня» относят Си, LLVM, C--.

Первые три поколения образуют императивную парадигму, последующие — декларативную: императивные языки описывают, как получить результат, декларативные — что требуется, оставляя выбор транслятору. Обычно языки высокого уровня дают код медленнее, но надёжность и скорость разработки важнее предельной скорости, поэтому выгоднее развивать оптимизирующую компиляцию; автоматическая сборка мусора считается важнейшим улучшением скорости разработки.

Безопасные и небезопасные языки

Представление данных числами в памяти вводит риск ошибок доступа к памяти, поэтому многие языки контролируют смысл операций через систему типов. Системы типов делятся на динамические (потомки Лиспа, Smalltalk) и статические — неполиморфные (потомки Алгола) и полиморфные (потомки ML); а также на сильные и слабые.

Язык называют безопасным, если принятые компилятором программы в динамике не выходят за рамки допустимого поведения. Безопасность одних языков (Standard ML) математически доказуема, а других (Ada) обеспечена ad hoc-образом; терминологию популяризировал Робин Милнер. Есть и языки для программ, верных по построению — Agda, Coq, — но их трудоёмкость растёт на порядки. Си и C++ небезопасны: в них обычны приведение типов и каламбур типизации, а системы статического анализа (PVS-Studio) обнаруживают не более 70—80 % ошибок.

Компилируемые, интерпретируемые и встраиваемые языки

Есть три способа реализации: компиляция, интерпретация и встраивание. Деление условно — язык с семантикой для интерпретации может реализовываться и оптимизирующими компиляторами (ML, Haskell). Компиляция преобразует исходный код в машинный код, давая исполнимый модуль; интерпретация выполняет код непосредственно; встраивание — «реализация без трансляции».

Скомпилированные программы обычно быстрее и автономны, но при изменении требуют перекомпиляции. Интерпретируемые запускаются сразу и на разных платформах ценой быстродействия. Языки вроде Java и C# занимают промежуточное положение: программа компилируется в байт-код, исполняемый виртуальной машиной (JVM, Common Language Runtime) с компиляцией «на лету».

Языки первого и высшего порядка

Языки первого порядка (потомки Алгола — Basic, классический Pascal) определяют лишь зависимости первого порядка — функции. Языки высшего порядка определяют функции высшего порядка (map f x зависит от функции f), рассматривая функции как объекты первого класса; термины ввёл Кристофер Стрэчи. К ним относятся почти все функциональные языки.

Языки первого порядка позволяют воплощать алгоритмы, но не архитектуру программ, поэтому схемы взаимодействия компонентов оформились в методологию шаблонов проектирования. Языки высшего порядка воплощают такие схемы в переиспользуемом коде, сокращая громоздкие фрагменты до одной команды. Это касается в основном языков на базе лямбда-исчисления (потомки Лиспа, ML), но программирование высшего порядка дают и стековые (Forth), и часть объектно-ориентированных (Smalltalk, CLOS).

Порядок языка оказывает психологическое влияние: владение высокоуровневыми языками помогает мыслить абстрактнее[5], а низкоуровневые могут навязывать обратное, с чем связано известное высказывание:

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

Само по себе использование языка высшего порядка не гарантирует улучшения архитектуры (см. серебряной пули нет) — решает умение применять нужные идиомы. Знание многих языков облегчает выбор инструмента и реализацию предметно-ориентированных языков[6].

Парадигма программирования

Отнесение языков к парадигмам ведётся по признакам — частью техническим, частью условным. Технически языки делятся на допускающие побочные эффекты и ссылочно-прозрачные; как парадигмы рассматривают и свойства системы типов или гомоиконичность, открывающую метапрограммирование. Многие языки формируют уникальные парадигмы: Лисп воплотил лямбда-исчисление, Smalltalk — объектно-ориентированную, а Forth — конкатенативную.

Более условно языки делятся на поколения: первые два — низкоуровневые, вместе с третьим образуют императивную парадигму, последующие — декларативную. С третьим поколением сложилось структурное и модульное программирование; к четвёртому относят функциональные языки, к пятому — логические. Многие парадигмы — условные методики организации структуры, применимые ко многим языкам, тогда как другие тесно связаны с техническими свойствами (шаблоны C++).

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

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

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

Martin Ward, «Language Oriented Programming»[7]

Тестируемость и модифицируемость определяются коэффициентом повторного использования. Параметрически полиморфные и динамические системы типов его повышают: функция длины массива применима к бесконечному множеству типов. Если же в сигнатуре требуется конкретная реализация данных, коэффициент падает: Pascal критиковали за обязательное указание размера массива, а C++ — за различение . и ->. Языки высшего порядка позволяют выделять схемы взаимодействия в функции высшего порядка, а максимум переиспользования достигается при переходе к специально разрабатываемому под задачу языку — переиспользуется язык, а не функция[7].

Помимо семантики, переиспользование обеспечивает модульная структура — определение интерфейса для каждого фрагмента и сокрытие реализации. Одни языки (Basic, классический Pascal Вирта) рассчитаны лишь на мелкие программы, Си как «высокоуровневый ассемблер» тоже не поддерживает крупный масштаб, а Erlang, Smalltalk, Prolog дают базовыми примитивами сложные концепции (процессы, классы, базы знаний). Standard ML расслаивается на «ядро» и «язык модулей»; Лисп и Forth наращиваются безгранично, за что их называют метаязыками. Самый популярный подход к комплексированию — объектно-ориентированное программирование, хотя достоверных данных о его преимуществе по качеству нет.

Концептуальная целостность языков

Фредерик Брукс[8] и Ч. Э. Р. Хоар подчёркивают необходимость концептуальной целостности — использования сходных синтаксических и семантических форм во всех частях системы (Хоар предсказывал, что сложность Ады станет причиной катастроф). Алан Кэй отделяет языки-«стиль во плоти» (англ. crystalization of style) от языков-«склеивания возможностей» (англ. agglutination of features)[9], а Эндрю Аппель[10] выделяет «языки, наследованные от математики» (англ. mathematically-derived languages).

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

Примеры математически обоснованных языков и воплощаемых ими моделей:

Математическое обоснование может гарантировать (или обещать с высокой вероятностью) ряд свойств:

Формальные преобразования и оптимизация

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

На небольших программах языки высокого уровня медленнее, но на сложных программах преимущество может быть за ними, так как человек не способен вручную учитывать всю специфику машинного исполнения (в одном бенчмарке MLton и Stalin Scheme опережают GCC). Автоматическая оптимизация в принципе даёт более высокую скорость, чем ручной контроль: автоматическое управление памятью эффективнее ручного (см. сборка мусора), а распределение регистров требует решения задачи раскраски графа. Таких особенностей машинной логики много, поэтому компиляция может включать десятки «шагов на уровень вниз».

Стратегий автоматической оптимизации много: одни универсальны, другие зависят от способа использования языка. Пример — оптимизация хвостовых вызовов: стандарт Scheme требует её гарантировать, а в Си и C++ она выполняется лишь в отдельных случаях. Языки высшего порядка обычно исполняются медленнее языков первого порядка — из-за декомпозиции кода в цепочку вложенных вызовов (см. замыкание), однако агрессивная оптимизация позволяет редуцировать их до языков первого порядка (см. дефункционализация).

Популярность языков

Определить самый популярный язык трудно, так как «популярность» зависит от контекста: один язык отнимает больше человеко-часов, на другом написано больше строк кода, третий занимает больше процессорного времени. Многие языки популярны для конкретных задач: Кобол используется в корпоративных дата-центрах, Фортран — в научных приложениях, потомки Си — в системном программировании.

Существуют разные метрики популярности, каждая со своим смещением:

  • число вакансий, упоминающих язык;
  • количество проданных книг;
  • оценка числа строк кода;
  • частота упоминаний в запросах поисковиков.

В 2020-х годах в комбинированных рейтингах уверенно лидирует Python: по индексу TIOBE он достиг пика в 26,98 % в июле 2025 года, а к февралю 2026 года его доля снизилась до 21,81 %, но он по-прежнему опережал ближайших конкурентов более чем на 10 процентных пунктов; языком года TIOBE по итогам 2025 года стал C#[12].

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

Примечания

  1. ISO/IEC/IEEE 24765:2010 Systems and software engineering — Vocabulary
  2. 1 2 3 Ахо А., Лам М., Сети Р., Ульман Дж. Компиляторы: принципы, технологии и инструментарий = Compilers: Principles, Techniques, and Tools. — 2-е изд.. — М.: Вильямс, 2008. — ISBN 978-5-8459-1349-4.
  3. Хопкрофт Дж., Мотвани Р., Ульман Дж. Введение в теорию автоматов, языков и вычислений = Introduction to Automata Theory, Languages, and Computation. — 2-е изд.. — М.: Вильямс, 2002. — ISBN 5-8459-0261-4.
  4. Winskel G. The Formal Semantics of Programming Languages: An Introduction. — Cambridge: MIT Press, 1993. — ISBN 978-0-262-23169-5.
  5. Пратт, Зелковиц, 2002, 1.1 Зачем изучать языки программирования, с. 17—18.
  6. Пратт, Зелковиц, 2002, 1.1 Зачем изучать языки программирования, с. 18.
  7. 1 2 Ward, 1994.
  8. Брукс, 1975, 1995, Достижение концептуальной целостности, с. 30.
  9. Алан Кэй. The Early History of Smalltalk. — Apple Computer, ACM SIGPLAN Notices, vol.28, № 3, March 1993. Архивировано 5 октября 2025 года.
  10. 1 2 Appel — A Critique of Standard ML, 1992.
  11. Брукс, 1975, 1995.
  12. TIOBE Index. TIOBE Software. Дата обращения: 2 июля 2026.

Литература

  • Пратт Т., Зелковиц М. Языки программирования: разработка и реализация. — 4-е изд.. — СПб.: Питер, 2002. — 688 с. — ISBN 5-318-00189-0.
  • Брукс Ф. Мифический человеко-месяц, или Как создаются программные системы = The Mythical Man-Month. — СПб.: Символ-Плюс, 1975, 1995. — ISBN 5-93286-005-7.
  • Ward M. P. Language Oriented Programming // Software — Concepts and Tools. — 1994. — Т. 15, № 4. — С. 147—161.
  • Appel A. W. A Critique of Standard ML. — Princeton University, CS-TR-364-92, 1992.

Категории