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

Что ж, позвольте внести ясность и предложить вам непротиворечивую классификацию парадигм — подходов к написанию кода, во многом определяющих способ мышления человека по донесению задачи до кремниевого исполнителя.
Аспекты классификации
Классифицировать парадигмы можно по разным аспектам..
🎯 Целеполагание
💫 Вычисление состояний
🙌 Согласование состояний
📜 Управление потоком исполнения
🔀 Диспетчеризация
🚻 Контроль типов
🎭 Обобщение алгоритмов
🎫 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 — Новости
А на этом пока что всё. С вами был.. классный пара-Димитрий Карловский.
ссылка на оригинал статьи https://habr.com/ru/articles/905292/
Добавить комментарий