Декларативные и Императивные языки программирования
Что такое парадигмы и для чего они нужны?
Парадигма - это набор идей и абстракций, которые позволяют нам понять, как следует структурировать наш код.
Для наглядного примера, можно взглянуть на физику, химию и другие научные дисциплины, которые помогают нам понять устройство мира вокруг нас. Физика использует математику для описания физических свойств объектов и их взаимодействия. Химия также оперирует математическими понятиями, чтобы описать взаимодействие химических элементов между собой. Однако химия более специфична и использует свои собственные методы описания химических процессов.
Точно так же и в программировании. Мы имеем общие концепции, такие как математика (как это есть в научных парадигмах), а также конкретные языки программирования, которые специализируются на определенных задачах и областях. Важно отметить, что без общих концепций, таких как математика, различные области знаний и наук были бы сложнее сочетать и взаимодействовать друг с другом. Математика, подобно общим парадигмам, служит языком, который позволяет разным дисциплинам легче обмениваться идеями и объяснять процессы, которые они исследуют. Благодаря этому, ученые и инженеры могут лучше понимать и описывать важные явления и создавать новые решения.
Какие парадигмы существуют?
На данный момент, практически все современные языки программирования высокого уровня которыми мы часто пользуемся совмещают в себе сразу несколько парадигм, но в основном являются императивными, за исключением функциональных языков. Некоторые из них возможно вам уже знакомы, а некоторыми вы возможно пользуетесь и даже не подозреваете об этом.
Наиболее популярные парадигмы:
- ООП (Объектное ориентированное программирование)
- ФП (Функциональное программирование)
- Декларативное программирование
- Императивное программирование
Примеры использования декларативной парадигмы
Язык который является, по моему мнению эталонным, когда мы говорим о декларативном подходе, является — SQL, да это не совсем язык программирования, но в нашей сфере нигде нет чёткого да и можно долго дискутировать на эту тему.
Вот банальный SQL запрос с которым наверное все повсеместно сталкиваются даже если пользуются ORM-Фреймворками:
SELECT * FROM `users` ORDER BY `id` DESC LIMIT 100;
В императивном стиле программирования мы даем точные инструкции, как выполнять задачу, например: "Достать 100 пользователей и отсортировать их по убыванию".
В декларативном стиле мы указываем, что хотим сделать, а детали реализации уже заранее заданы, например: "Получить список пользователей в порядке убывания, ограничиваясь 100 пользователями."
Декларативный стиль делает код более легким для понимания, так как мы сконцентрированы на задаче, а не на том, как именно ее выполнить.
SELECT users.name, orders.product FROM users INNER JOIN orders ON users.id = orders.user_id WHERE users.country = 'USA' ORDER BY users.name;
Мы хотим получить имена пользователей из США и названия продуктов, которые они заказали, отсортированные по имени пользователя.
В декларативном стиле мы описываем, что мы хотим получить, и какие условия применить (пользователи из США и сортировка по имени). Детали того, как именно выполнить JOIN и выполнить выборку, определяются внутри SQL-запроса, но в самом запросе мы устанавливаем только "что" и "по каким правилам".
Примеры использования императивной парадигмы
Мы рассматривали примеры декларативного подхода, и говорили о том, что императивный подход отличается тем что мы сами задаём как будет происходить получение пользователей из базы данных, теперь давайте рассмотрим лёгкий пример императивного подхода, для более лучшего понимания я воспользуюсь JavaScript и как раз подмечу ещё пару интересных фактов.
Вот наш запрос в декларативном подходе:
SELECT * FROM `users` ORDER BY `id` DESC LIMIT 100;
Теперь давайте рассмотрим как бы это выглядело если мы писали всё в императивном стиле:
// Предположим, что у нас есть массив объектов (бд) // представляющих пользователей const users = [ { id: 3, name: 'Пользователь 3', ... }, { id: 1, name: 'Пользователь 1', ... }, { id: 2, name: 'Пользователь 2', ... }, // ... ]; for (let i = 0; i < users.length - 1; i++) { for (let j = i + 1; j < users.length; j++) { if (users[i].id < users[j].id) { // Обмен местами const temp = users[i]; users[i] = users[j]; users[j] = temp; } } } // Ограничиваем результат 100 пользователями (декларативный подход) const limitedUsers = users.slice(0, 100); // Выводим результат console.log(limitedUsers);
Давайте разберём что тут происходит, у нас есть массив с пользователями (представим что это наша база данных), мы сортируем её, описывая алгоритм сортировки, после отрезаем 100 пользователей и выводим результат.
Когда в декларативном стиле мы говорим что нам нужно, тут мы говорим как сделать так чтобы получить нужное. Собственно все мы и пишем обычно в императивном стиле, но вы могли заметить, что на строчке где мы ограничиваем количество пользователей вырезая только 100 объектов из нашей "базы данных", в комментарии указано что это декларативный подход.
JavaScript имеет встроенные функции для работы с массивами, что позволяет использовать готовые алгоритмы, не переписывая их многократно. Это делает код более декларативным. Например, можно удалить написанный нами алгоритм сортировки и вместо него использовать встроенную функцию sort
.
// Сортируем пользователей в порядке убывания по id users.sort((a, b) => b.id - a.id);
И тут у нас появляется идеальный пример того, что из императивного стиля мы превратили код в декларативный, в нашем понимании, так как тут мы пусть и пользуемся стандартным синтаксисом нашего языка, но мы чётко говорим функции, что нам нужно:
- Оператор
-
можно воспринимать какDESC
или жеASC
в языке SQL sort
можно воспринимать какORDER BY
b.id - a.id
как указатель на то что мы хотим отсортировать
Заключение
В заключении статьи следует подчеркнуть важность библиотек и фреймворков, которые упрощают разработку на императивных языках программирования. Эти инструменты значительно облегчают нашу работу и позволяют использовать декларативный подход без необходимости многократного переписывания одного и того же кода.
Библиотеки и фреймворки предоставляют нам готовые решения, абстрагируя детали реализации и позволяя нам сконцентрироваться на описании того, что нужно сделать, вместо того, как это сделать. Это упрощает код и делает его более читаемым и понятным.
Современные языки программирования часто совмещают в себе множество парадигм, потому что разработчики сталкиваются с разными задачами, и каждая парадигма имеет свои преимущества. Сочетание различных подходов позволяет выбирать наиболее подходящий стиль программирования для конкретной задачи. Это дает разработчикам большую гибкость и позволяет создавать более эффективное и читаемое программное обеспечение.
В итоге, понимание и умение применять как императивные, так и декларативные подходы к программированию делает разработчиков более компетентными и позволяет им выбирать наилучший инструмент для решения конкретных задач, что способствует улучшению качества и производительности программного обеспечения.