Здравствуйте, меня зовут Дмитрий Карловский и я.. придерживаюсь следующей парадигмы мышления: всякое определение должно иметь чёткую границу между тем, что ему соответствует, и тем, что не соответствует.
К сожалению, часто можно встретить споры о пересекающихся определениях, словно они взаимоисключают друг друга. Не менее часто можно встретить ложную дилемму между двумя терминами не покрывающими всё множество сущностей.

Что ж, позвольте внести ясность и предложить вам непротиворечивую классификацию парадигм - подходов к написанию кода, во многом определяющих способ мышления человека по донесению задачи до кремниевого исполнителя.
Аспекты классификации
Классифицировать парадигмы можно по разным аспектам..
🎯 Целеполагание
💫 Вычисление состояний
🙌 Согласование состояний
📜 Управление потоком исполнения
🔀 Диспетчеризация
🚻 Контроль типов
🎭 Обобщение алгоритмов
🎫 Cинхронизация задач
Все аспекты ортогональны, но парадигмы в рамках одного аспекта взаимоисключающи. Это, впрочем, не мешает многим языкам поддерживать разные парадигмы даже в рамках одного аспекта - тут разработчик сам выбирает в какой парадигме писать код.
Большой список парадигм можно найти на Вики, но мы его использовать не будем. Тем не менее, по возможности будут приведены ссылки на соответствующие вики-статьи, чтобы вы могли сами сравнить нашу писанину и решить кто правее.
🎯 Целеполагание
Целеполагание | Языки | Вики |
⛳ Декларативное | Prolog, HTML, многие DSL и тд. | |
🎢 Императивное | Почти все языки общего назначения именно такие. |
⛳ Декларативное целеполагание
Описание результата, которого исполнитель может достигать разными способами.
X(a,b,c): a*X^2 + b*X + c = 0
🎢 Императивное целеполагание
Конкретные инструкции исполнителю по достижению результата.
X(a,b,c) := ( -b ± sqrt( b^2 - 4*a*c ) )/( 2*a )
Обратите внимание, что в примере императивного кода представлена как раз чистая функция. Не редко можно встретить заблуждение, что функциональное программирование относится к декларативной парадигме.
Появилось оно, по всей видимости, ввиду ссылочной прозрачности чистых функций, то есть возможности прозрачной замены выражения на его результат и, как следствие, возвожности переставлять операнды местами. А исходное выражение тогда как бы описывает результат, не задавая конкретный порядок вычислений.
Однако, это не отменяет того, что описание это представлено не в форме декларирования свойств результата, а в форме конкретного алгоритма, где, как минимум частично, порядок вычислений всё же задан порядком вложенности вызовов функций. А единственный способ получить результат — так или иначе вычислить функцию.
💫 Парадигмы вычисления состояний
Вычисление | Языки | Вики |
⏩ Функциональное | F#, Haskell, D и тд. | |
🔄 Процедурное | C/++, Python, D и тд. |
⏩ Функциональное вычисление
Все состояния гарантированно не изменяются после инициализации, поэтому код описывает лишь создание новых состояний на базе существующих.
one := 1 // one = 1
next( prev ) := prev + one
two := next( one ) // two = 2
🔄 Процедурное вычисление
Состояние может меняться со временем в процессе работы кода.
state := 1 // state = 1
state := state + 1 // state = 2
Обратите внимание, что во многих языках процедуры, которые могут возвращать значение, называются функциями. Не дайте себя запутать, такие функции не имеют никакого отношения к функциональной парадигме, в которой функции употребляются в более строгом смысле, в соответствии с математической чистотой. Даже если эти процедуры являются сущностями первого класса и высшего порядка, это всё же не делает процедурное программирование функциональным.
Также стоит подчеркнуть, что даже если процедура не обращается к изменяемым состояниям, это всё ещё не позволяет рассматривать её как чистую функцию. Последняя именно не может к ним обратиться, что позволяет получать качественную выгоду от чистоты.
🙌 Парадигмы согласования состояний
Согласование | Языки | Вики |
🏓 Интерактивное | Все не реактивные | |
🚀 Реактивное | Elm, Verilog и тд. |
🏓 Интерактивное согласование
Непосредственное взаимодействие со всеми связанными состояниями.
// инициализация состояний
name := "Jin"
limit := 4
short := name.length < limit
// внесение одних изменений
name := "Jan"
short := name.length < limit
// внесение других изменений
limit := 3
short := name.length < limit
🚀 Реактивное согласование
Автоматическое поддержание заданных инвариантов (правил, не меняющихся в процессе работы программы).
// инициализация состояний
name := "Jin"
limit := 4
// настройка инвариантов
short <= name.length < limit
// применение изменений
name := "Jan" // short = true
limit := 3 // short = false
Обратите внимание, что реактивность - это не просто выполнение кода в ответ на какое-то событие, как в событийном программировании, а именно реакция на изменение одного состояния, чтобы каскадно обновить все зависящие от него.
🔀 Парадигмы диспетчеризации
Диспетчеризация | Языки | Вики |
💎 Прямая | C, WASM и др. | — |
🔱 Объектная | Является основой всех ООП языков: C#, Java, JS и тд. | |
🔮 Множественная | CLOS, С++, D и тд. |
💎 Прямая диспетчеризация
Конкретные реализации жёстко задаются в коде вызова.
sum_int( 1, 2 )
sum_float( 1.1, 2.2 )
sum_float( 1.1, float_from_int( 2 ) )
🔱 Объектная диспетчеризация
Структура данных определяет реализации методов для работы с нею.
( 1 ).add_int( 2 ) // int:add_int
( 1.1 ).add_int( 2 ) // float:add_int
( 1.1 ).add_float( 2.2 ) // float:add_float
Такая структура с явными или неявными ссылками на реализации методов называется объектом или капсулой. А использующий её код становится за счёт этого полиморфным.
🔮 Множественная диспетчеризация
Наиболее специфичная реализация выбирается на основе типов аргументов.
sum( 1, 2 ) // sum_int_int
sum( 1.1, 2 ) // sum_float_int
sum( 1, 2.2 ) // sum_int_float
sum( 1.1, 2.2 ) // sum_float_float
Если типы известны на этапе компиляции, то это называется перегрузкой функций, если же известны лишь в рантайме, то это называется мультиметодами.
Так же стоит отметить UFCS, позволяющий вызывать функцию, словно она является методом первого аргумента. Однако, это всё же не объектная диспетчеризация, а лишь синтаксический сахар для множественной.
🎭 Парадигмы обобщения алгоритмов
Обобщение | Языки | Вики |
📦 Коробочное | Java, JS и тд. | |
🎎 Шаблонное | C++, D |
Плюс на Вики есть отдельная статья про обобщённое программирование.
📦 Коробочное обобщение
Разные типы заворачиваются в обобщённую коробку, а единожды сформированный код работает уже с любыми типами через обобщённый интерфейс этой коробки.
dump( obj ) := log( obj.toString() )
dump( new Object( 1 ) )
dump( new Object( 1.1 ) )
🎎 Шаблонное обобщение
Код повторяется компилятором множество раз с подстановкой разных типов.
dump( Type )( val ) := log( Type.stringify( val ) )
dump( int )( 1 )
dump( float )( 1.1 )
🚻 Парадигмы контроля типов
Типизация | Языки | Вики |
⚓ Статическая | C/++, D и тд. | |
🎡 Динамическая | JS, Python и тд. | |
🧤 Ручная | Assembler, Forth и тд. |
⚓ Статическая типизация
Типы декларируются в коде и проверяются до его исполнения.
name : string := 1 // ошибка статического анализа: 1 - не строка
🎡 Динамическая типизация
Типы определяются и проверяются лишь в процессе исполнения кода.
count := 1
cout.sendEmail() // рантайм исключение: неизвестный метод
🧤 Ручная типизация
Используется один универсальный тип без ограничений и проверки его структуры - всё это отдаётся на откуп прикладному разработчику.
sendEmailTo( new Date ) // падение с непредсказуемым результатом
📜 Парадигмы управления потоком исполнения
Поток исполнения | Языки | Вики |
🌌 Свободный | Assembler и другие низкоуровневые. | — |
🗼 Структурный | Большинство современных языков высокого уровня. |
🌌 Свободный поток исполнения
Произвольный переход к любому участку кода.
goto short_log if name = ""
short := name.length < limit
short_log:
log short // переменная может быть не задана
🗼 Структурированный поток исполнения
Жёсткие правила перехода между блоками кода: куда, как и на каких условиях возможен переход.
short_log:
if( name = "" ) break short_log
short := name.length < limit
log short // переменна точно задана
Важно отметить, что структурированные языки не обязательно лишены оператора goto. Например, в D он сильно ограничен, чтобы в тех редких случаях, когда он необходим, его использование было безопасным.
🎫 Парадигмы синхронизации сопрограмм
Cинхронизация | Языки | Вики |
📞 Явная | C#, JS и тд. | |
🎠 Неявная | Go, D и тд. |
📞 Явная синхронизация
Явное ожидание завершения вложенной сопрограммы иначе она будет исполняться асинхронно.
pipe( input, output ) // исполняется асинхронно
data := await input.read() // явно дожидаемся завершения
🎠 Неявная синхронизация
Автоматическая приостановка текущий сопрограммы до завершения вложенной, если она явно не запущена асинхронно.
go pipe( iput, output ) // явно запускаем асинхронно
data := input.read() // приостанавливаемся до завершения
Продолжение следует..
Список парадигм не полный и, скорее всего, многие аспекты тут не рассмотрены. Наверняка я забыл что-то важное, так что предлагайте и свои дополнения к данной классификации.
core__dump — Видео
hyoo — Поддержка
h_y_o_o — Обсуждения
mam_mol — Новости
А на этом пока что всё. С вами был.. классный пара-Димитрий Карловский.