Введение

Высокоуровневая концептуализация компьютерного программирования и классификация языков программирования.

Парадигма программирования – это относительно высокоуровневый способ структурирования и концептуализации реализации компьютерной программы. Язык программирования может быть классифицирован как поддерживающий одну или несколько парадигм. Парадигмы разделяются и описываются различными аспектами программирования. Некоторые парадигмы связаны с особенностями модели исполнения, например, разрешением побочных эффектов или определением последовательности операций моделью исполнения. Другие парадигмы касаются организации кода, например, группировки в единицы, включающие как состояние, так и поведение. Ещё одни парадигмы относятся к синтаксису и грамматике. Некоторые распространенные парадигмы программирования включают (представлены в иерархических взаимосвязях):
Императивное программирование – код напрямую управляет потоком выполнения и изменением состояния.
Процедурное программирование – организовано в виде процедур, вызывающих друг друга.
Объектно-ориентированное программирование – организовано в виде объектов, содержащих как структуру данных, так и связанное с ней поведение.
Декларативное программирование – код описывает свойства желаемого результата, но не способ его вычисления.
Функциональное программирование – желаемый результат представляется как значение серии вычислений функций.
Логическое программирование – желаемый результат представляется как ответ на вопрос о системе фактов и правил.
Реактивное программирование – желаемый результат представляется с использованием потоков данных и распространения изменений.

Обзор

Парадигмы программирования берут начало в исследованиях компьютерных наук, посвященных существующим практикам разработки программного обеспечения. Полученные результаты позволяют описывать и сравнивать практики программирования и языки, используемые для написания программ. Для лучшего понимания, другие исследования изучают процессы разработки программного обеспечения и описывают различные методологии для их описания и сравнения. Язык программирования можно описать с точки зрения парадигм. Некоторые языки поддерживают только одну парадигму. Например, Smalltalk поддерживает объектно-ориентированную, а Haskell – функциональную. Большинство языков поддерживают несколько парадигм. Например, программа, написанная на C++, Object Pascal или PHP, может быть чисто процедурной, чисто объектно-ориентированной или содержать элементы обеих или других парадигм. При использовании языка, поддерживающего несколько парадигм, разработчик выбирает, какие элементы парадигмы использовать. Однако этот выбор не всегда предполагает осознанное рассмотрение самих парадигм. Разработчик часто использует возможности языка по мере их доступности и в той мере, в которой он с ними знаком. Категоризация получившегося кода по парадигме часто является академическим упражнением, выполняемым задним числом. Языки, классифицируемые как императивные парадигмы, имеют две основные характеристики: они определяют порядок выполнения операций, используя конструкции, явно контролирующие этот порядок, и допускают побочные эффекты, когда состояние может быть изменено в один момент времени внутри одного блока кода, а затем прочитано в другой момент времени внутри другого блока кода. Взаимодействие между блоками кода не является явным. В отличие от этого, языки декларативной парадигмы не указывают порядок выполнения операций. Вместо этого они предоставляют набор доступных операций в системе вместе с условиями, при которых каждая из них может быть выполнена. Механизм исполнения языка отслеживает, какие операции могут быть выполнены, и самостоятельно выбирает порядок их выполнения. Подробнее – в статье «Сравнение многопарадигматических языков программирования». В объектно-ориентированном программировании код организован в объекты, содержащие состояние, которое принадлежит и (обычно) контролируется кодом объекта. Большинство объектно-ориентированных языков также являются императивными. В объектно-ориентированном программировании программы рассматриваются как набор взаимодействующих объектов. В функциональном программировании программы рассматриваются как последовательность вычислений функций без состояния. При программировании компьютеров или систем с множеством процессоров, в процессеориентированном программировании программы рассматриваются как наборы параллельных процессов, действующих на логические структуры данных. Многие парадигмы программирования известны не только поддерживаемыми ими методами, но и теми, которые они запрещают. Например, чисто функциональное программирование запрещает побочные эффекты, а структурированное программирование – конструкцию goto. Отчасти поэтому новые парадигмы часто воспринимаются как догматичные или излишне жесткие теми, кто привык к старым. Однако, избегая определенных методов, можно упростить понимание поведения программы и доказать теоремы о ее корректности. Парадигмы программирования также можно сравнивать с моделями программирования, которые позволяют вызывать модель исполнения, используя только API. Модели программирования также могут быть классифицированы по парадигмам на основе характеристик модели исполнения. Для параллельных вычислений часто используется модель программирования вместо языка. Это связано с тем, что детали параллельного оборудования проникают в абстракции, используемые для программирования этого оборудования. Это вынуждает программиста сопоставлять шаблоны в алгоритме с шаблонами в модели исполнения (которые были внедрены из-за проникновения аппаратных деталей в абстракцию). В результате ни один язык параллельного программирования не подходит для решения всех вычислительных задач. Поэтому удобнее использовать базовый последовательный язык и вставлять вызовы API в параллельные модели исполнения через модель программирования. Такие модели параллельного программирования можно классифицировать в соответствии с абстракциями, отражающими аппаратное обеспечение, такими как общая память, распределенная память с передачей сообщений, представления о месте, видимые в коде, и так далее. Эти можно рассматривать как разновидности парадигм программирования, применимые только к параллельным языкам и моделям программирования.

Критика

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

История

Со временем развивались различные подходы к программированию. Классификация каждого подхода либо описывалась одновременно с его разработкой, либо, чаще, ретроспективно, спустя некоторое время. Одним из первых подходов, сознательно обозначенных как таковой, является структурированное программирование, которое продвигалось начиная с середины 1960-х годов. Концепция парадигмы программирования в том виде, в каком мы её знаем, возникла, по крайней мере, в 1978 году, в лекции Роберта Флойда при вручении премии Тьюринга под названием «Парадигмы программирования», где он ссылается на понятие парадигмы, использованное Томасом Куном в его работе «Структура научных революций» (1962). Ранние языки программирования не имели чётко определённых парадигм, и программы часто интенсивно использовали операторы `goto`. Чрезмерное использование этих операторов приводило к так называемому "спагетти-коду", который сложно понимать и поддерживать. Это послужило стимулом для разработки парадигм структурированного программирования, которые запрещали использование операторов `goto`, разрешая лишь более структурированные управляющие конструкции.

Код машины

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

Сборка

Язык ассемблера ввел мнемонические обозначения для машинных инструкций и адресов памяти. Ассемблер классифицируется как императивный язык и иногда называется языком программирования второго поколения. В 1960-х годах языки ассемблера были разработаны для поддержки библиотечной функции COPY и достаточно сложных возможностей условной генерации макросов и предварительной обработки, вызова подпрограмм (CALL), использования внешних переменных и общих областей (глобальных переменных), что обеспечивало значительное повторное использование кода и изоляцию от аппаратных особенностей посредством логических операторов, таких как READ/WRITE/GET/PUT. Ассемблер использовался и продолжает использоваться для систем, критичных ко времени отклика, и часто во встраиваемых системах, поскольку он обеспечивает максимальный контроль над действиями машины.

Языки процедур

Процедурные языки, также называемые языками программирования третьего поколения, первыми были описаны как языки высокого уровня. Они используют лексику, соответствующую решаемой задаче. Например, в языке COmmon Business Oriented Language (COBOL) используются такие термины, как файл, переместить и копировать. FORmula TRANslation (FORTRAN), использующий терминологию математического языка, был разработан главным образом для научных и инженерных задач. ALGOrithmic Language (ALGOL) был ориентирован на то, чтобы стать подходящим языком для определения алгоритмов, при этом используя математическую терминологию и предназначенный для научных и инженерных задач, как и FORTRAN. Programming Language One (PL/I) – гибридный язык общего назначения для коммерческих и научных вычислений, поддерживающий указатели. Beginners All-purpose Symbolic Instruction Code (BASIC) был разработан, чтобы сделать программирование доступным для большего числа людей. C – язык программирования общего назначения, первоначально разработанный Деннисом Ричи в период с 1969 по 1973 год в AT&T Bell Labs. Эти языки классифицируются как процедурная парадигма. Они напрямую управляют пошаговым процессом выполнения компьютерной программы. Таким образом, эффективность и результативность такой программы во многом зависят от квалификации программиста.

Объектно-ориентированное программирование

В попытке улучшить процедурные языки были созданы языки объектно-ориентированного программирования (ООП), такие как Simula, Smalltalk, C++, Eiffel, Python, PHP, Java и C#. В этих языках данные и методы для работы с этими данными находятся в едином кодовом блоке, называемом объектом. Эта инкапсуляция обеспечивает единственный способ доступа объекта к данным – через методы самого объекта, содержащего эти данные. Таким образом, внутреннее устройство объекта может быть изменено без воздействия на код, использующий этот объект. Александр Степанов, Ричард Столлман и другие программисты подняли вопрос о целесообразности парадигмы ООП по сравнению с процедурной парадигмой. Необходимость наличия у каждого объекта связанных методов заставляет некоторых скептиков связывать ООП с разрастанием программного кода; попыткой разрешить эту дилемму стал полиморфизм. Хотя большинство языков ООП относятся к третьему поколению, возможно создание объектно-ориентированного ассемблера. High Level Assembly (HLA) является примером такого языка, полностью поддерживающего продвинутые типы данных и объектно-ориентированное программирование на ассемблере, несмотря на свои ранние истоки. Таким образом, различные парадигмы программирования можно рассматривать скорее как вдохновляющие мемы, отражающие убеждения их сторонников, чем как непременный прогресс от одного уровня к другому. Точные сравнения эффективности конкурирующих парадигм часто затруднены из-за новой и различной терминологии, применяемой к схожим сущностям и процессам, а также многочисленных различий в реализации на разных языках.

Декларативные языки

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

Другие парадигмы

Символическое программирование — это парадигма, описывающая программы, способные манипулировать формулами и компонентами программ как данными. Дифференцируемое программирование структурирует программы таким образом, чтобы их можно было дифференцировать целиком, как правило, с помощью автоматического дифференцирования. Литературное программирование, как форма императивного программирования, структурирует программы как ориентированную на человека сеть, подобно гипертекстовому эссе: документация является неотъемлемой частью программы, а программа структурирована в соответствии с логикой изложения, а не удобством компилятора. Символические методы, такие как рефлексия, позволяющие программе ссылаться на саму себя, также можно рассматривать как парадигму программирования. Однако это совместимо с основными парадигмами и, следовательно, не является самостоятельной парадигмой.