Думаю, вы, несомненно, слышали об императивном и декларативном программировании. Возможно, вы даже хотели узнать, что в действительности обозначают эти термины.
К сожалению, вы, вероятно, сталкивались с определением вроде этого: «Дескать, вы знаете, императивное программирование похоже на то, как вы что-то делаете, а декларативное программирование больше на то, что вы делаете или что-то в этом духе».
Данное определение имеет смысл, если вы действительно понимаете разницу между императивным и декларативным — но это не так, именно поэтому вы задаетесь этим вопросом. Это напоминает попытку решить дилемму курицы и яйца, за исключением того, что кажется, что все думают, что сначала была курица, но вам даже яйца не нравятся и вы в замешательстве.
Объедините это разочарование с «падением» самого слова «декларативный» до простого «хорошо» и внезапно ваш «синдром самозванца» станцует чечетку на вашей же уверенности, отчего вы можете сделать вывод, что не очень-то и любите программировать.
Не волнуйтесь, друзья. Я не знаю, что такое монада (monad), поэтому, вероятно, эта публикация поможет понять, что декларативный стиль — это нечто большее, чем просто «легко порассуждать о чем-то» и «хорошо«.
Сложность этой темы, как заметил Merrick, в том, что «это одна из тех вещей, которые вы понимаете интуитивно, но не можете объяснить».
Я говорил со многими разработчиками, и, похоже, больше всего тут помогает сочетание метафор с реальными примерами кода. Так что пристегнитесь, потому что я собираюсь #проповедовать.
Вернемся к изначальному определению, над которым я уже смеялся:
Императивное программирование похоже на то, «как» вы что-то делаете, а декларативное программирование больше похоже на «что» вы делаете».
Вообще, здесь припрятано немного хорошей информации. Сначала разберем достоинства этого определения вне контекста программирования и рассмотрим пример из «реальной жизни».
Вы решаете, что тратите слишком много времени на споры об «изнурительном JavaScript» («JavaScript Fatigue» ™) и реактивном функциональном программировании (Reactive Functional Programming), и ваш супруг достоин хорошего свидания. Вы решили пойти в Red Lobster, так как вы слушали много Бейонсе в последнее время. Вы приезжаете в Red Lobster, подходите к стойке и говорите…
Императивный подход (КАК): я вижу, что стол под знаком Gone Fishin’ свободен. Мы бы хотели подойти к нему и занять.
Декларативный подход (ЧТО): Стол для двоих, пожалуйста.
Императивный подход связан с тем, как вы собираетесь получить место. Вам нужно перечислить шаги, чтобы показать, как вы собираетесь занять стол. Декларативный подход больше про то, что вы хотите, про стол на двоих.
«Ок.» — ваш мозг.
Больше метафор!
Задам вопрос и хочу, чтобы вы подумали об «императивном» и «декларативном» ответе.
«Я из Wal-Mart. Как мне добраться до вашего дома отсюда?»
Императивный ответ: выйдите с северного выхода паркинга и поверните налево. Затем по I-15 на юг, пока не доберетесь до съезда с Bangerter Highway. Поверните направо, как будто вам нужно в ИКЕА. Идите прямо и поверните направо на первом светофоре. Двигайтесь дальше к следующему светофору, поверните налево. Мой дом 298.
Декларативный ответ: мой адрес — 298 West Immutable Alley, Draper Utah 84020.
Вне зависимости от того, как я доберусь до вашего дома, машину-то я вожу. Собираюсь ли я водить «императивную машину «на ручке» или «декларативную автоматическую машину». Хватит метафор?
Прежде чем мы углубимся в код, важно понять, что многие декларативные подходы имеют своего рода императивный слой абстракции. Пример:
Декларативный ответ сотруднику Red Lobster предполагает, что тот знает все необходимые шаги, чтобы разместить нас. Знание адреса предполагает, что у вас есть какой-то GPS, который в курсе императивных шагов, как добраться до вашего дома.
Автоматический автомобиль имеет какой-то уровень абстракции над переключением передач.
Это было осознание, которое действительно заставило «щелкнуть» для меня, поэтому повторю: многие (если не все) декларативные подходы имеют в основе какую-то императивную абстракцию.
Если это предложение имеет для вас смысл, то вы молодец!
Теперь попытаемся совершить прыжок из «рая метафор» в «реальность» кода. Чтобы сделать этот маневр более изящным, давайте посмотрим на некоторые «языки» программирования, которые скорее декларативны по своей сути по сравнению с теми, которые императивны по своей природе.
- Императивные: C, C++, Java;
- Декларативные: SQL, HTML;
- Могут сочетать в себе оба подхода: JavaScript, C#, Python.
Подумайте здесь о типичном примере на SQL или HTML:
Изучив оба примера, у вас появится очень четкое понимание происходящего. Они оба декларативные. Они «обеспокоены» тем, ЧТО вы хотите сделать, а не КАК.
Вы описываете, чего вы пытаетесь достичь, не указывая как. Реализация выбора всех пользователей из Мексики была «абстрагирована». Вас не интересует, как веб-браузер анализирует вашу статью и отображает ее на экране. Ваше — это то, ЧТО такое пользователи из Мексики и новый заголовок и абзац на вашем сайте.
Все идет прекрасно! Перейдем к практической части на JavaScript.
Представьте, что вы на техническом собеседовании, а я интервьюер. Откройте консоль и решите задачи:
Напишите функцию double, которая принимает массив чисел и возвращает новый массив после удвоения каждого элемента в этом массиве. double([1,2,3]) -> [2,4,6]
Напишите функцию add, которая принимает массив и возвращает результат сложения элементов массива. add ([1,2,3]) -> 6
Используя jQuery (или vanilla JavaScript), добавьте обработчик событий click к элементу с id="btn"
. При нажатии нужно переключить (добавить или удалить) класс подсветки, а также изменить текст, чтобы добавить подсветку или удалить подсветку в зависимости от текущего состояния элемента.
Рассмотрим наиболее распространенные подходы, которые также могут быть императивными:
Изучив, что общего у этих трех императивных примеров, мы сможем лучше определить, что на самом деле делает их императивными.
Наиболее «очевидное общее» у них в описании — это КАК что-то делать. В каждом примере мы либо явно производим итерацию по массиву, либо явно излагаем шаги для реализации нужной нам функциональности.
Это может быть не столь очевидно, если вы не привыкли думать декларативно, или даже более конкретно — функционально. В каждом примере мы мутируем некоторую часть состояния (если Вы не знакомы с термином состояние (state), то это в основном информация о чем-то, что хранится в памяти, что должно звучать как очень похожее на переменные). В первых двух примерах мы создаем переменную results, а затем постоянно модифицируем ее. В третьем примере у нас нет переменных, но у нас есть состояние, живущее в DOM и затем мы изменяем это состояние в DOM.
Прозвучит немного субъективно, но для меня код выше не очень читаем. Я не могу так просто взглянуть на код и понять, что происходит. Мой мозг должен пройти сквозь код словно интерпретатор, принимая во внимание контекст, в котором живет код (еще один «минус» мутированных данных).
Ладно, хватит уже bullshita’ в коде) Рассмотрим некоторые декларативные примеры. Идея в том, чтобы исправить все проблемы сверху. Таким образом, каждый пример должен описывать, ЧТО происходит, не может мутировать состояние и должен легко читаться с первого взгляда.
Гораздо лучше!
Обратите внимание, что в первых двух примерах мы использовали методы map и reduce «из коробки» JavaScript. Это восходит к тому, о чем мы говорим снова и снова в этой статье, что большинство декларативных решений являются абстракцией над некоторой императивной реализацией.
В каждом примере мы описываем то, ЧТО
мы хотим, а не КАК
(мы не знаем, КАК реализованы map и reduce, но нам это и не важно). Мы не мутируем state. Все мутации абстрагированы в map и reduce. Это также и более читабельно (как только вы привыкнете к map и reduce, конечно).
А как насчет № 3? Ну, я немного обманул вас и использовал React, но обратите внимание, что все три императивные ошибки исправлены. Настоящая красота React в том, что вы можете создавать декларативные UI. Глядя на наш компонент Btn, я могу легко понять, как будет выглядеть UI. Другое выгодное отличие — вместо того, чтобы состояние (state) «жило» в DOM, оно «живет» в компоненте React.
Еще одним преимуществом декларативного кода является то, что программа может быть независимой от контекста. Это означает, что, поскольку ваш код связан с конечной целью, а не с шагами, которые необходимы для достижения цели, один и тот же код может быть переиспользован в разных программах и прекрасно работать.
Посмотрите на все три примера выше. Мы можем использовать как функции, так и компоненты в любой программе. Они программно-агностические. Это трудно сделать с императивным кодом, потому что часто, по определению, императивный код опирается на контекст текущего состояния.
Одна вещь, которую я не рассматривал слишком детально, — это то, что функциональное программирование является подмножеством декларативного программирования. Если вы еще этого не проходили, я настоятельно рекомендую ознакомиться с методами функционального программирования в JavaScript. Начните с .map, .reduce, .filter и «разрабатывайте план действий» с них.
Улучшить ваш код, сделав его более функциональным, как говорится, «не сложнее низко висящих фруктов» (или проще, чем «взять с полки пирожок» — по-русски, в тексте приведено английское популярное выражение — прим.переводчика).
Ниже я привел некоторые другие определения из сети, которые могут быть полезными. Или не быть.
- Декларативное программирование — это «акт программирования на языках, которые соответствуют ментальной модели разработчика, а не операционной модели машины».
- Декларативное программирование — это программирование с помощью декларирования, т. е. декларативных предложений.
- Декларативное свойство — это свойство, в котором может существовать только один возможный набор выражений, которые могут экспрессировать каждую конкретную модульную семантику. Императивное свойство двойственно: семантика противоречива по составу и / или может быть выражена с вариациями наборов выражений.
- Декларативные языки «контрастируют» с императивными языками, которые указывают на явное управление внутренним состоянием компьютера, или процедурными языками, которые специфицируют четкую последовательность действий.
- В информатике декларативное программирование — это парадигма программирования, которая выражает логику вычисления без описания его потока управления.
Перевод: Freeemasons
Оригинал: Imperative vs Declarative Programming
Спасибо за перевод. Топ статья
А то я слыхал, что reduce это декларативная версия цикла. Но как-то не доходила суть этой фразы.
JavaScript допускает как парадигмы императивного, так и декларативного программирования. Большая часть кода JS, который мы читаем и пишем, императивная. Однако, с ростом популярности функционального программирования в JS, декларативные подходы распространяются все больше.