Функциональное программирование, 2016, Луис Атенсио. Часть 1 — Думайте функционально

Весьма вероятно, что основная часть вашего опыта по разработке профессиональных приложений была связана с объектно-ориентированными свойствами языка. Возможно, вы слышали или читали о функциональном программирование в других книгах, блогах, в статьях на форумах и журналах, но вы наверное, никогда не писали какого-либо функционального кода. Не волнуйтесь, это следовало ожидать. Я провел большую часть своего развития в объектно ориентированной среде. Писать функциональный код не сложно, но нужно научиться думать функционально и отпустить свои старые привычки. Первая часть этой книги нужна для организации и подготовки своего ума, чтобы охватить функциональные методы, рассмотренные во второй и третьей частях.

В главе 1 рассматривается что такое функциональное программирование и как надо мыслить для его понимания. Она также представляет некоторые из самых важных методов на основе чистых функций, неизменность, побочные эффекты и ссылочная прозрачность. Они формируют основу всего функционального кода что поможет вам понять функциональное программирование без труда. Кроме того, они будут ведущими принципами, которые устанавливают этапы для многих проектов, которые мы будем делать в следующих главах.

Глава 2 содержит первое представление JavaScript в качестве функционального языка. Потому что он повсеместно используется и это идеальный язык для изучения функционального программирования. Если вы не сильный разработчик JavaScript, то эта глава наиболее быстро донесет до вас все необходимые знания для понимания функционального JavaScript, такие как функции высшего порядка, замыкания и области видимости.

Функциональное становление

  • Мышление в функциональном отношении
  • Понимание что такое функциональное программирование и зачем оно нужно
  • Понимание принципов неизменности и чистых функций
  • Функциональные методы программирования и их влияние на общий вид проекта

Объектно-ориентированное программирование делает код понятным путем инкапсуляции меняющихся частей. Функциональное программирование делает код понятным путем сведения к минимуму количества меняющихся частей. Майкл Фезес

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

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

Как разработчики, мы стремимся по направлению к фреймворкам, которые помогают нам создавать расширяемые и чистые архитектуры приложения. Тем не менее, сложность нашего кода возрастает и мы вынуждены пересмотреть основные принципы проектирования нашего кода. Кроме того, веб сегодня радикально отличается от того, чем он был несколько лет назад для JavaScript разработчиков, потому что мы можем сделать много вещей сейчас, которые были технически неосуществимы раньше. Мы можем выбрать для написания больших серверных приложений Node.JS или выдвинуть основную часть кода на клиентскую сторону, оставляя немного за сервером. В любом случае, нам нужно взаимодействовать с технологией хранения, вызовами асинхронных процессов, обработки событий и многое другое.

Объектно-ориентированное проектирование помогает решить часть проблемы, но поскольку JavaScript является динамическим языком с множеством разделенного состояния, то пройдет не так много, прежде чем код дойдет до такого состояния, что сделает наш код громоздким и трудно поддерживаемым. Объектно-ориентированное проектирование конечно перемещает разработку в правильном направлении, но нам нужно больше. Возможно вы слышали в последние годы термин «Реактивное программирование». Эта парадигма программирования облегчает работу с потоками данных и распространение изменений. В JavaScript это особенно важно при работе с асинхронным кодом или кодом на основе событий. В общем, что нам нужно, это парадигма программирования, которая побуждает нас тщательно продумать наши данные и функции, которые взаимодействуют с ними. Когда вы думаете о проектирование приложения, задайте себе следующие вопросы с точки зрения этих принципов проектирования:

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

Если вы ответили «да» или «я не знаю» на любой из вопросов, то вы сделали правильный выбор книги как руководство на пути к продуктивности. Функциональное программирование (ФП) — парадигма программирования которая вам нужна. Несмотря на то, что она основана на простых концепциях, ФП требует изменения мышления касательно проблем. ФП не является новым инструментом или API, однако это другой подход к решению проблемы, который станет понятным после того, как вы понимаете основные принципы.
В этой главе я дам определение функциональному программированию и расскажу вам почему это полезно и важно знать. Я представлю основные принципы неизменности и чистые функции, поговорим о методах ФП и как эти методы влияют на ваш подход к проектирование приложений. Эти методы позволяют легко выбрать реактивное программирование и использовать его для решения сложных задач в JavaScript. Но прежде чем мы разберемся во всем этом, вы должны узнать, почему функциональное мышление так важно и как оно может помочь вам решить сложные задачи в JavaScript.

1.1 Чем может помочь функциональное программирование?

Изучение функционального программирования никогда не была так важно, как сегодня. Сообщества разработчиков и крупные компании по разработке программ начинают осознавать преимущества использования методов ФП для поддержки своих бизнес-приложений. В настоящее время большинство больших языков программирования (Scala, Java 8, F#, Python, JavaScript, и многие другие) предоставляют собственную или на основанную на API функциональную поддержку. Следовательно, навыки ФП высоко востребованы сейчас и так будет в ближайшие годы.

В контексте JavaScript, а также мышление ФП могут быть использованы для формирования невероятно выразительной природы языка и помогает вам написать код чистым, модульным, тестируемым и кратким, поэтому вы сможете работать более продуктивно. В течение многих лет мы пренебрегали тем фактом, что JavaScript позволяет писать код более эффективно в функциональном стиле. Такое упущение следует из-за неполного понимания языка JavaScript, а также из-за отсутствия в JavaScript собственных конструкций для должного управления состоянием. Это динамическая платформа, которая возлагает все усилия управления состоянием на нас. Это может работать хорошо для небольших скрипты, но это становится все труднее контролировать, как ваш код возрастает. Я думаю ФП защищает вас от самого JavaScript. Об этом я рассказываю в дальнейшей главе 2.

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

Поскольку функциональное программирование не платформа или инструмент, а способ написания кода, функциональное мышление кардинально отличается от мышления в объектно-ориентированном стиле. Но как же стать функциональным? Как же начать думать функционально? Функциональное программирование становится интуитивно понятным, как только вы понимаете его суть. Отвыкание от старых привычек — самая трудная часть, и может быть огромным изменением парадигм для большинства людей, которые приходят с объектно-ориентированной подготовкой. Прежде чем вы научитесь мыслить функционально, во-первых вы должны узнать, чем является ФП.

1.2 Что такое функциональное программирование?

Проще говоря, функциональное программирование — стиль разработки программного обеспечения, в котором главный акцент помещается на использование функций. Вы можете сказать: «Ну, я уже использую функции изо дня в день при разработке. В чем разница?». Как я уже упоминал ранее, ФП требует, чтобы вы думали немного иначе, касательно поставленных задач. Это не просто применение функций для достижения результата, скорее это абстрактное управление потоками и операциями над данными с использованием функций для того, чтобы избежать побочных эффектов и уменьшение изменений состояния в вашем приложении. Я знаю что это звучит как бессмыслица, но я буду возвращаться к каждому из этих терминов в дальнейшем и опираться на них на протяжении всей книги.

Как правило, книги по ФП начинаются с вычисления чисел Фибоначчи, но я бы предпочел начать с помощью простой программы на JavaScript, который отображает текст на HTML-странице. Какой лучше текст напечатать, кроме как старого доброго “Привет мир”:

document.querySelector('#msg').innerHTML = '<h1>Hello World</h1>';

 

Примечание Я упоминал ранее, что вследствие того, что функциональное программирование не является каким то определенным инструментом, а лишь способом написания кода, то вы можете применить его для написания кода на стороне клиента (с использованием браузера), а также серверных приложений (Node.js). Открыв браузер и набрав в нем какой-то код, вероятно, самый простой способ попасть в среду JavaScript и запустить его, и это все, что вам нужно для этой книги.

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

function printMessage(elementId, format, message) {
document.querySelector(`#${elementId}`).innerHTML =
`<${format}>${message}</${format}>`;
}
printMessage('msg', 'h1','Hello World');

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

Листинг 1.1 Функциональный подход printMessage

var printMessage = run(addToDom('msg'), h1, echo);
printMessage('Hello World');

Несомненно, внешне это радикально отличается от оригинала. Во-первых h1 уже не скалярная величина, это просто функция как addToDom и echo. Визуально, это выглядит, как будто вы создаете функцию из небольших функций.
Есть причина для этого безумия. Листинг 1.1 отражает процесс разложения программы на более мелкие части, которые являются более универсальными, надежнее и проще для понимания, а потом, объединив их, сформировав целую программу, которую легче разобрать как все целое. Каждая функциональная программа следует этому фундаментальному принципу. На данный момент, вы будете использовать магическую функцию, run, чтобы последовательно вызвать целый ряд функций, таких как addToDom, h1 и echo. Подробно о run я объясню пожже. Негласно, это в основном связывает каждую функцию подобно цепочке, при возвращении первого значения как входные данные для следующего. В нашем случае строка «Hello World» вернувшись из echo была передана в h1, и в конечном итоге передана в addToDom.

Почему функциональное решение выглядеть таким образом? Мне нравится думать о нем, как фактически параметризирует ваш код, так что вы можете изменить его «неразрушаемым способом» скорректировав начальные условия алгоритма.

Листинг 1.2 Расширенная printMessage

var printMessage = run(console.log, repeat(3), h2, echo);
printMessage('Get Functional');

Этот визуально отличающийся подход тут не случайно. При сравнении функционального и нефункционального решения, вы, возможно, заметили что существует огромная разница в стиле. Оба отображают одно и тоже, но выглядят по разному. Это происходит из-за присущего ФП декларативного режима разработки. Для того, чтобы полностью понять функциональное программирование, вы должны сначала изучить основные понятия, на которых оно основано:

  • Декларативное программирование
  • Чистые функции
  • Ссылочная прозрачность
  • Неизменяемость

1.2.1 Функциональное программирование является декларативным

Функциональное программирование является одной из парадигм декларативного программирования — это парадигма, которая выражает совокупность операций, не раскрывая, каким образом они реализованы или как данные протекают через них. Хотя есть более популярные модели, используемые сегодня, императивная или процедурная, и поддерживаются в большинстве структурированных или объектно-ориентированных языках, как Java, C#, C++ и других. Императивное программирование обрабатывает компьютерная программа как простую последовательность операторов от начала до конца, которая изменяет состояние системы, чтобы вычислить результат.

Давайте посмотрим на простой пример императивного подхода. Предположим, вам нужно возвести в квадрат все числа в массиве. Императивная программа выполняет следующие действия:

var array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
for(let i = 0; i < array.length; i++) {
array[i] = Math.pow(array[i], 2);
}
array; //-> [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Императивное программирование сообщает компьютеру, в мельчайших подробностях, как выполнить определенную задачу (В данном случае перебрать и применить формулу квадрата для каждого числа). Это наиболее распространенный способ написания этого кода и, скорее всего, ваш первый подход к решению этой проблемы.

Декларативное программирование, с другой стороны, отделяет описание программы от оценки. Она фокусируется на использовании выражений, чтобы описать то, что логика без необходимости уточнения его управление потоком или изменением состояния. Пример декларативного программирования можно найти в SQL операторах. SQL запросы состоят из утверждений, которые описывают то, как результат запроса должен быть выглядеть, абстрагируясь на внутренний механизм для извлечения данных. В главе 3 я покажу пример использования SQL-подобного наложения поверх вашего функционального кода, чтобы придать значение вашему приложению и данным, которые проходят через него.

Переходя к функциональному подходу решения этой задачи вам нужно всего лишь иметь дело с применением правильного поведения на каждом элементе и передать управление циклом другим частям системы. Вы можете позволить Array.map() сделать большую часть тяжелой работы:

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(
function(num) {
return Math.pow(num, 2);
});
//-> [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

По сравнению с предыдущим примером, вы видите, что этот код освобождает вас от обязанности надлежащего управления счетчиком цикла и доступа к индексам массива; проще говоря, чем больше кода, тем больше мест для возникших ошибок. Кроме того, стандартные циклы не являются многоразовыми артефактами, если они не абстрагировались с функциями. И это именно то, что мы будем делать. В главе 3 я покажу как вручную удалять циклы полностью из вашего кода, в пользу объектов первого класса функций высшего порядка как map, reduce и filter,  которые принимают функции в качестве параметров, что делает ваш код более универсальным, расширяемым и декларативным. Это то, что я делал с волшебной функцией run в листинге 1.1 и 1.2.

Абстрактные циклы с функциям позволяют вам воспользоваться преимуществами лямбда-выражений и стрелочных функций введенных в ES6 JavaScript. Лямбда-выражения предоставляют краткую альтернативу анонимным функциям, которые можно передать в качестве аргумента функции.

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(num => Math.pow(num, 2));
//-> [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
Перевод  лямбда-выражения к обычной функции.Лямбда-выражения предоставляют огромное синтаксические преимущество по сравнению с обычной записью функции, потому что они уменьшают структуру вызова функции вплоть до самых важных частей. Это лямбда-выражение в ES6:

num => Math.pow(num, 2)

эквивалентно следующей функции:

function(num) {
return Math.pow(num, 2);
}

Зачем удалять циклы из кода?  Цикл является обязательной структурой, которую трудно использовать повторно и подключить к другим операциям. Кроме того, он подразумевает, что код постоянно меняется или видоизменяется после каждой итерации.  Вы узнаете, что функциональные программы направлены на как можно большую неизменность и простоту. Простой код имеет нулевую вероятность изменения или нарушения глобального состояния. Для достижения этой цели, вы будете использовать функции, которые позволяют избежать побочных эффектов и изменений состояния, известные как чистые функции.

1.2.2 Чистые функции и проблема с побочными эффектами

Функциональное программирование основано на предположение что вы создаете неизменные программы основанные на строительных блоках из чистых функций. Чистая функция обладает следующими качествами:

  • Это зависит только от предоставленных данных, а не на каких-либо скрытых или внешних состояний, которые могут меняться в ходе их определения или между вызовами.
  • Это не вносит изменения за пределы их области видимости, такие как изменение глобального объекта или переданного параметра.

Интуитивно, любая функция, которая не отвечает этим требованиям, является «нечистой». Программирование с неизменностью может показаться странным на первый взгляд. В конце концов, вся суть императивного дизайна, который является тем, к чему мы привыкли, где объявленные переменные видоизменяются из одного состояния к другому. Это для нас нормальное поведение. Рассмотрим следующую функцию:

var counter = 0;
function increment() {
return ++counter;
}

Эта функция является нечистой, потому что он читает/изменяет внешнюю переменную counter, который не является локальной для этой функции.  Как правило, функции имеют побочные эффекты при чтении или записи внешних данных, как это показано на рисунке 1.1. Другим примером может служить популярная функция Date.now (); ее результат, конечно, не предсказуем и последователен, потому что это всегда зависит от постоянно меняющихся фактора: время.

В случае, counter доступен через неявное обращение к глобальной переменной (в браузере с JavaScript, это window object). Другой распространенный побочный эффект возникает при доступе к данным экземпляра с помощью ключевого слова. Такое поведение в JavaScript не на какой-либо другой язык программирования, потому что он определяет контекст выполнения функции.  Это часто приводит к сложно понимаемому коду, чего я избегаю, когда это возможно. Я вернусь к этой теме в следующей главе. Побочные эффекты могут возникнуть во многих ситуациях, в том числе:

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

Продолжение следует…

Написать комментарий

Ваш Email не будет опубликован

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