JavaScript: Побочные эффекты
console.log() выводит что-то на экран, но это не возврат значения — это просто какое-то действие, которое выполняет функция.
Вывод на экран и возврат значения из функции — разные и независимые операции. Технически вывод на экран равносилен записи в файл (немного особый, но всё-таки файл). Для понимания этой темы необходимо немного разобраться в устройстве операционных систем, что крайне важно для программистов.
С точки зрения программы вывод на экран — это так называемый побочный эффект. Побочным эффектом называют действия, которые взаимодействуют с внешним окружением (средой выполнения). К таким действиям относятся любые сетевые взаимодействия, взаимодействие с файловой системой (чтение и запись файлов), вывод информации на экран, печать на принтере и так далее.
Побочные эффекты — один из основных источников проблем и ошибок в программных системах. Код с побочными эффектами сложен в тестировании и ненадежен. При этом без побочных эффектов программирование не имеет смысла. Без них было бы невозможно получить результат работы программы (записать в базу, вывести на экран, отправить по сети и так далее).
Понимание принципов работы с побочными эффектами очень сильно влияет на стиль программирования и способность строить качественные программы. Эта тема полностью раскроется в курсах на Хекслете.
Вопрос для самопроверки. Можно ли определить наличие побочных эффектов внутри функции, опираясь только на её возврат?
Задание
Это задание не связано напрямую с уроком
Советы
Определения
Цикл for хорошо послужил нам, но он устарел и должен уступить место более новым, функциональным техникам программирования.
К счастью, этот факт не требует от вас быть мастером функционального программирования, более того, это то, что вы можете использовать в своих текущих проектах прямо сегодня!
Так в чем проблема цикла for в JavaScript?
Дизайн цикла for подталкивает к мутациям состояния (англ. mutation of state — изменение состояния — прим. переводчика) и применению сайд эффектов (англ. side effects — побочные эффекты — прим. переводчика), которые являются потенциальными источниками багов и непредсказуемого поведения кода.
Все мы слышали, что глобальное состояние — это плохо, и что мы должны избегать его. Однако, локальное состояние разделяет те же проблемы что и глобальное, мы просто не сталкиваемся с ними так часто, ведь они проявляются в меньших масштабах. Фактически, мы никогда не решали проблему, а просто сводили ее к минимуму.
Однажды, используя мутабельное состояние (англ. mutable state — изменяемое состояние — прим. переводчика), значение случайной переменной изменится по неизвестной причине, и вы потратите часы на отладку и поиск причины изменения. У меня волосы на голове встают дыбом, только от одной мысли об этом.
Я бы хотел немного поговорить о сайд эффектах. Эти слова даже звучат ужасно, сайд эффекты. Дрянь. Вы хотите чтобы в ваших программах были сайд эффекты? Нет, я не хочу сайд эффектов в моих программах!
Но что такое сайд эффекты?
Считается, что у функции есть сайд эффекты если она модифицирует что-то за пределами своей области видимости. Это может быть изменение переменной, пользовательский ввод, запрос к api, запись данных на диск, лог в консоль и т.д.
Сайд эффекты — действительно мощный инструмент, но с большой силой приходит большая ответственность.
Сайд эффекты, по возможности, должны быть устранены или хотя бы инкапсулированы так, чтобы мы могли их контролировать. Функции с сайд эффектами тяжелее читать и тестировать, избегайте их всегда, когда это возможно. К счастью, в рамках данной статьи, мы не будем беспокоиться о побочных эффектах.
Я собираюсь отрефакторить этот код шаг за шагом, чтобы вы смогли пронаблюдать как легко превратить ваш собственный код в нечто более прекрасное.
Во-первых, я извлеку условное выражение в отдельную функцию:
Выносить условия — это в целом хорошая практика. Изменение фильтрации с “меньше чем 7 месяцев” на “является ли котенком” — большой шаг вперед. Теперь код передает наши намерения гораздо лучше. Почему мы берем котов до 7 месяцев? Не совсем понятно. Мы хотим найти котят, так пусть код говорит об этом!
Другая польза в том, что isKitten теперь можно переиспользовать, а все мы знаем, переиспользование кода всегда должно быть нашей целью.
Следующее изменение — извлечь преобразование (или отображение) кота его в имя. Это изменение будет понятнее позднее, а сейчас просто доверьтесь мне.
Код отдающий предпочтение const перед var и let выглядит чертовски привлекательно
Конечно, мы могли использовать const с самого начала, так как он не делает сам объект иммутабельным (об этом больше в другой раз), но это придуманный пример, не наседайте!
И последнее изменение, я бы предложил также извлечь фильтрацию и отображение в функцию (для полного переиспользования кода).
Домашнее задание
Изучить извлечение методов filter и map из их объектов. Задание со звездочкой: исследовать композицию функций.
Что насчет break
Многие из вас спрашивали, “Что насчет break ”, посмотрите часть вторую серии “Переосмысление JavaScript: break это GOTO циклов”.
Заключение
Для вас это мелочь, но меня очень радует когда кто-то подписывается на меня на медиуме или в твиттере (@joelnet), а если вы считаете мой рассказ бесполезным, то расскажите об этом в комментариях.
Чистые функции — JS: Функции
Функции в программировании обладают рядом важных характеристик. Зная их, мы можем точнее определять, как лучше разбивать код на функции и когда вообще их стоит выделять.
Детерминированность
Встроенная в JavaScript функция Math.random() возвращает случайное число от 0 до 1:
Функция нужная и полезная, но неудобная в отладке и тестировании. Это связано с тем, что для одних и тех же входных аргументов (отсутствие аргументов также попадает под это понятие), она может возвращать разные значения. Функции с таким поведением называются недетерминированными.
Например, недетерминированными являются функции, оперирующие системным временем. Так, функция Date.now() каждый раз возвращает новое значение:
Хотя прямо сейчас повторный запуск вернёт точно такое же значение, через год оно уже будет другим. То есть функция считается недетерминированной, если она ведёт себя так хотя бы единожды.
Детерминированные функции, напротив, ведут себя предсказуемо. Для одних и тех же входных данных они всегда выдают один и тот же результат. Именно такими являются функции в математике.
Функция становится недетерминированной и в том случае, если она обращается не только к своим аргументам, но и некоторым внешним данным, например глобальным переменным, переменным окружения и так далее. Так происходит потому, что внешние данные могут измениться, и функция начнёт выдавать другой результат, даже если в неё передаются одни и те же аргументы.
В общем случае нельзя сказать, что отсутствие детерминированности — абсолютное зло. Для работы многих программ и сайтов нужна функция, возвращающая случайное число или вычисляющая текущую дату. С другой стороны, в нашей власти разделить код так, чтобы в нем было как можно больше детерминированных частей. Общая рекомендация при работе с детерминированностью звучит следующим образом: если есть возможность написать функцию так, что она будет детерминированной, то так и делайте. Не используйте глобальных переменных, создавайте функции, зависящие только от своих собственных аргументов.
Понятие «Детерминированность» не ограничивается программированием или математикой. Сквозь него можно рассматривать практически любой процесс. Например, подбрасывание монетки — недетерминированный процесс, его результат случаен.
Побочные эффекты (side effects)
Вторая ключевая характеристика функций — наличие побочных эффектов. Побочными эффектами называют любые взаимодействия с внешней средой. К ним относятся файловые операции, такие как запись в файл, чтение файла, отправка или приём данных по сети и даже вывод в консоль.
Кроме того, побочными эффектами считаются изменения внешних переменных (например, глобальных) и входных параметров в случае, когда они передаются по ссылке.
А вот вычисления (логика), напротив, не содержат побочных эффектов. Например, функция, суммирующая два переданных аргументами числа.
Побочные эффекты составляют одну из самых больших сложностей при разработке. Их наличие значительно затрудняет логику кода и тестирование. Приводит к возникновению огромного числа ошибок. Только при работе с файлами количество возможных ошибок измеряется сотней: начиная с того, что закончилось место на диске, заканчивая попыткой читать данные из несуществующего файла. Для их предотвращения код обрастает большим числом проверок и защитных механизмов.
Без побочных эффектов невозможно написать ни одной полезной программы. Какие бы важные вычисления она ни делала, их результат должен быть как-то продемонстрирован. В самом простом случае его нужно вывести на экран, что автоматически приводит нас к побочным эффектам:
В реальных же приложениях, обычно, все сводится к взаимодействию с базой данных или отправкой запросов по сети.
Не существует способа избавиться от побочных эффектов совсем, но их влияние на программу можно минимизировать. Как правило, в типичной программе побочных эффектов не так много по отношению к остальному коду, и происходят они лишь в самом начале и в конце. Например, программа, которая конвертирует файл из текстового формата в PDF, в идеале выполняет ровно два побочных эффекта:
Между этими двумя пунктами и происходит основная работа, которая содержит чистую алгоритмическую часть. Побочные эффекты, в таком случае, будут находиться только в верхнем слое приложения, а ядро, выполняющее основную работу, останется чистым от них.
Инкремент и декремент — единственные базовые арифметические операции в JS, которые обладают побочными эффектами (изменяют само значение в переменной). Именно поэтому с ними сложно работать в составных выражениях. Они могут приводить к таким сложноотлавливаемым ошибкам, что во многих языках вообще отказались от их введения (в Ruby и Python их нет). В JS стандарты кодирования предписывают их не использовать.
Чистые функции
Идеальная функция с точки зрения удобства работы с ней называется чистой (pure). Чистая функция — это детерминированная функция, которая не производит побочных эффектов. Такая функция зависит только от своих входных аргументов и всегда ведёт себя предсказуемо.
Чистые функции обладают рядом ключевых достоинств:
Открыть доступ
Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно.
Справочник современных концепций JavaScript: часть 1
Основы функционального программирования, реактивного программирования и функционального реактивного программирования на JavaScript
Кратко: В первой части серии «Справочник современных концепций JavaScript» мы познакомимся с функциональным программированием (FP), реактивным программированием (RP) и функциональным реактивным программированием (FRP). Для этого мы узнаем о чистоте, состоянии и его отсутствии, неизменяемости и изменяемости, императивном и декларативном программировании, функциях высшего порядка, observables и парадигмах FP, RP, FRP.
Введение
За последние годы JavaScript сильно развился и не думает на этом останавливаться. Однако множество концепций, рождающихся в JS-блогах и документациях, так и остаются невостребованными широким кругом разработчиков интерфейсов. В настоящей серии статей мы изучим концепции, требующие как среднего, так и продвинутого уровней подготовки, а также рассмотрим, как они вписываются в современный JavaScript.
Концепции
В этой статье мы рассмотрим концепции, имеющие решающее значение для понимания функционального программирования, реактивного программирования и функционального реактивного программирования и их использования с JavaScript:
Чистота
Чистые функции
Возвращаемое значение чистой функции зависит только от ее входных данных (аргументов) и не влечет никаких побочных эффектов. С одним и тем же входящим аргументом результат всегда будет одинаковый. Пример:
Чистые функции зависят только от того, что им передано. Например, чистая функция не может ссылаться на переменные из области видимости родителя, если они явно не передаются в нее в качестве аргументов. Но и даже тогда функция не может изменять что-либо в родительской области видимости.
Нечистые функции
Нечистая функция изменяет состояние вне своей области видимости. Любые функции с побочными эффектами (см. далее) — нечистые, ровно как и процедурные функции без возвращаемого значения.
Рассмотрим следующие примеры:
Побочные эффекты в JavaScript
Когда функция или выражение изменяет состояние вне своего контекста, результат является побочным эффектом. Примеры побочных эффектов: вызов API, манипулирование DOM, вывод alert, запись в базу данных и так далее. Если функция производит побочные эффекты, она считается нечистой. Функции, вызывающие побочные эффекты, менее предсказуемы и их труднее тестировать, поскольку они приводят к изменениям вне их локальной области видимости.
Подводя итог: чистота
Много качественного кода состоит из нечистых функций, процедурно вызывающихся чистыми. Это все равно несет массу преимуществ для тестирования и неизменяемости. Ссылочная прозрачность также обладает удобством для мемоизации: кэширование и сохранение результатов вызова функций, а затем переиспользование кэшированных результатов. Однако определить, когда функции действительно чисты, может быть непросто.
Дополнительную информацию о чистоте можно найти на следующих ресурсах:
Состояние
Состояние — информация, к которой программа имеет доступ и с которой может работать в определенный момент времени. Сюда входят данные, хранящиеся в памяти, порты ввода/вывода, базы данных и так далее. Например, содержимое переменных в приложении в любой данный момент времени репрезентативно для состояния приложения.
С состоянием
Программы, приложения или компоненты с состоянием хранят в памяти данные о текущем состоянии. Они могут изменять состояние, а также имеют доступ к его истории. Следующий пример демонстрирует это:
Без состояния
Функции или компоненты без состояния выполняют задачи, словно каждый раз их запускают впервые. Они не ссылаются или не используют в своем исполнении раннее созданные данные. Отсутствие состояния обеспечивает ссылочную прозрачность. Функции зависят только от их аргументов и не имеют доступа, не нуждаются в знании чего-либо вне их области видимости. Чистые функции не имеют состояния. Пример:
Приложения без состояния все еще управляют состоянием. Однако они возвращают свое текущее состояние без изменения предыдущего состояния. Это принцип функционального программирования.
Подводя итог: состояние
Управление состоянием важно для любого сложного приложения. Функции или компоненты с состоянием изменяют состояние и его историю, их труднее тестировать и отлаживать. Функции без состояния полагаются только на свои входные данные для создания данных выходных. Программа без состояния возвращает новое состояние, а не модифицирует существующее состояние.
Дополнительную информацию о состоянии можно найти на следующих ресурсах:
Неизменяемость и изменяемость
Концепции неизменяемости и изменяемости более туманны в JavaScript, чем в некоторых других языках программирования. Тем не менее, вы много услышите о неизменяемости при чтении о функциональном программировании в JS. Важно знать, что эти термины означают в классическом понимании, и как они реализуются в JavaScript. Определения достаточно просты:
Неизменяемый
Если объект является неизменяемым, его значение не может быть изменено после создания.
Изменяемый
Если объект изменяем, его значение может быть изменено после создания.
Реализация: неизменяемость и изменяемость в JavaScript
В JavaScript строки и числовые литералы реализованы неизменяемыми. Это легко понять, если рассмотреть, как мы работаем с ними:
Числовые литералы также неизменяемы. Следующий пример всегда будет иметь одинаковый результат:
Это демонстрирует, что в JavaScript присутствует реализация неизменяемости. Однако разработчики JS знают, что язык позволяет изменить многое. Например, объекты и массивы изменяемы. Рассмотрим следующий пример:
В этих примерах исходные объекты изменены. Новые объекты не возвращаются.
Чтобы узнать больше об изменяемости в других языках, ознакомьтесь с Изменяемые и неизменяемые объекты.
На практике: изменяемость в JavaScript
Функциональное программирование в JavaScript хорошо развивается. Но по своей сущности JS — очень изменчивый язык, состоящий из множества парадигм. Ключевая особенность функционального программирования — неизменяемость. Другие функциональные языки выбросят ошибку, когда разработчик попытается изменить неизменяемый объект. Тогда как мы можем примирить врожденную изменяемость JS при написании функционального или функционального реактивного JS?
Когда мы говорим о функциональном программировании в JS, слово «неизменяемое» используется много, но разработчик обязан всегда держать ее в голове. Например, Redux полагается на одно неизменяемое дерево состояний. Однако сам JavaScript способен изменять объект состояния. Чтобы реализовать неизменяемое дерево состояний, нам нужно каждый раз при изменении состояния возвращать новый объект состояния.
Существуют также библиотеки, поддерживающие неизменяемость в JS. Mori предоставляет постоянные структуры данных на основе Clojure. Immutable.js от Facebook также предоставляет неизменяемые коллекции для JS. Библиотеки утилит, такие как Underscore.js и lodash, предоставляют методы и модули для более функционального стиля программирования (а стало быть направленного на неизменяемость).
Подводя итог: неизменяемость и изменяемость
В целом, JavaScript — язык с сильной изменяемостью. Некоторые стили JS-кодирования опираются на эту врожденную изменяемость. Однако, при написании функционального JS, реализация неизменяемости требует внимательности. Если вы что-то нечаянно модифицируете, JS не будет выбрасывать ошибки. Тестирование и библиотеки могут помочь, но работа с неизменяемостью в JS требует практики и методологии.
Неизменяемость имеет свои преимущества. В результате получается код, который проще понимать. Он также обеспечивает персистентность, возможность хранения более старых версий структур данных и копирование только изменившихся частей.
Недостатком неизменяемости является то, что многие алгоритмы и операции не могут быть эффективно реализованы.
Дополнительную информацию о неизменяемости и изменяемости можно найти на следующих ресурсах:
Императивное и декларативное программирование
Хотя некоторые языки были разработаны как императивные (C, PHP) или декларативные (SQL, HTML), JavaScript и другие (такие как Java и C# могут поддерживать обе парадигмы программирования.
Императивное программирование
Императивное программирование описывает логику работы программы в явных командах с операторами, изменяющими состояние программы.
Рассмотрим функцию, увеличивающую каждое число в массиве целых чисел. Императивным примером в JavaScript может быть:
Эта функция описывает логику работы как: мы выполняем обход массива и явно увеличиваем каждое число, помещая его в новый массив. Затем мы возвращаем результирующий массив. Это пошаговое описание логики функции.
Декларативное программирование
Декларативное программирование описывает, что выполняет логика программы без описания того, как это сделать.
Очень простой пример декларативного программирования может быть продемонстрирован с помощью SQL. Мы можем запросить таблицу базы данных ( People ) для людей с фамилией Smith следующим образом:
Этот код легко читается и описывает то, что мы хотим достичь. Нет описания того, как результат должен быть достигнут. Компьютер об этом позаботится сам.
Мы показываем, что хотим достичь, но не как это работает. Метод Array.map() возвращает новый массив с результатами выполнения обратного вызова для каждого элемента из переданного массива. Этот подход не изменяет существующие значения и не включает в себя последовательную логику, раскрывающую, как он создает новый массив.
Подводя итог: императивное и декларативное программирование
JavaScript допускает как парадигмы императивного, так и декларативного программирования. Большая часть кода JS, который мы читаем и пишем, императивная. Однако, с ростом популярности функционального программирования в JS, декларативные подходы распространяются все больше.
Декларативное программирование имеет очевидные преимущества в отношении краткости и читаемости, но в тоже время может походить на магию. Многие новички в JavaScript могут на базе накопленного опыта писать императивный JS перед глубоким погружением в декларативное программирование.
Дополнительную информацию об императивном и декларативном программировании можно найти на следующих ресурсах:
Функции высшего порядка
Функция высшего порядка — это функция, которая принимает другую функцию в качестве аргумента или возвращает функцию в результате.
В JavaScript функции являются объектами первого класса. Они могут храниться и передаваться как значения: мы можем присвоить функцию переменной или передать функцию другой функции.
Обратный вызов — один из примеров использования функции в качестве аргумента. Обратные вызовы могут быть встроенными анонимными функциями или именованными функциями:
Мы также можем передать функцию в качестве аргумента любой другой созданной нами функции, а затем выполнить этот аргумент:
Функции высшего порядка также могут возвращать другую функцию:
Подводя итог: функции высшего порядка
Природа JavaScript функций как объектов первого класса делает их основой функционального программирования в JS.
Дополнительную информацию о функциях высшего порядка можно найти на следующих ресурсах:
Функциональное программирование
Теперь мы узнали о чистоте, отсутствии состояния, неизменяемости, декларативном программировании и функциях высшего порядка. Все эти концепции важны для понимания парадигмы функционального программирования.
На практике: функциональное программирование на JavaScript
Функциональное программирование охватывает приведенные выше концепции следующими способами:
Примечание: если бы мы попытались написать JavaScript веб-приложение, состоящее только из чистых функций без побочных эффектов, оно не смогло бы взаимодействовать с окружением и поэтому не было бы особенно полезным.
Давайте рассмотрим пример. Скажем, у нас есть текст, и мы хотим посчитать количество слов в нем. Мы также хотим найти ключевые слова длиной более пяти символов. Используя функциональное программирование, наш результирующий код может выглядеть примерно так:
Подводя итог: функциональное программирование
Неизменяемость данных и отсутствие состояния гарантируют, что состояние программы не изменяется. Вместо этого возвращаются новые значения. Чистые функции используются для функциональности ядра. Чтобы запустить программу и обработать необходимые побочные эффекты, нечистые функции могут императивно вызывать чистые.
Дополнительную информацию о функциональном программировании можно найти на следующих ресурсах:
Observables
Observables похожи на массивы, за исключением того, что элементы не хранятся в памяти, а поступают асинхронно с течением времени (потоки). Мы можем подписаться на observables и реагировать на их события. JavaScript оbservables — это реализация шаблона observer. Реактивные расширения (обычно называемые с префиксом Rx) предоставляют observables для JS через RxJS.
Для демонстрации концепции observables давайте рассмотрим простой пример: изменение размера окна браузера. В этом контексте observables максимально понятны. Изменение размера окна браузера испускает поток событий в течение определенного периода времени (пока окно принимает нужный размер). Мы можем создать observable и подписаться на него, чтобы реагировать на поток событий изменения размера:
Пример кода выше демонстрирует, что, по мере изменения размера окна, мы можем ограничивать observable поток и подписаться на его изменения, чтобы реагировать на новые значения в коллекции. Это пример горячего observable.
Горячие observable
Пользовательские интерфейсные события (нажатие кнопки, перемещения мыши и т.д.) — горячие. Горячие observable всегда будут срабатывать, даже если мы специально не реагируем на них подпиской. Пример изменения размера окна выше — горячий observable: resize$ observable отрабатывает вне зависимости от того, существует подписка или нет.
Холодные observable
Холодные observable срабатывают только тогда, когда мы подписываемся на него. Если мы подпишимся снова, он начнет все сначала.
Давайте создадим observable массив чисел от 1 до 5 :
Подводя итог: observables
Мы можем работать с observables по-разному. RxJS использует множество операторов. Observables часто визуализируется с помощью точек на линии, как показано на сайте RxMarbles. Поскольку поток состоит из асинхронных событий с течением времени, легко осмыслять это линейным способом и использовать именно такие зрительные образы, чтобы понять реактивные операторы. Например, следующие изображение от RxMarbles иллюстрирует оператор фильтра:
Дополнительную информацию об observables можно найти на следующих ресурсах:
Реактивное программирование
Реактивное программирование связано с декларативным (что делать, а не как) наблюдением и реагированием на поступающие события во времени.
Реактивное программирование часто связано с Reactive Extensions, API для асинхронного программирования с observable потоками. Реактивные расширения (с префиксом Rx) предоставляют библиотеки для различных языков, включая JavaScript (RxJS).
На практике: реактивное программирование на JavaScript
Вот пример реактивного программирования с observables. Предположим, у нас есть вход, где пользователь может ввести шестизначный код подтверждения, и мы хотим вывести его, когда будет введено последнее число. Наш HTML может выглядеть так:
Мы будем использовать RxJS и для реализации нашей функциональности создадим поток входных событий, вот так:
Подводя итог: реактивное программирование
Парадигма реактивного программирования включает наблюдение и реагирование на события в асинхронных потоках данных. RxJS используется в Angular и набирает популярность как решение JavaScript для реактивного программирования.
Дополнительную информацию о реактивном программировании можно найти на следующих ресурсах:
Функциональное реактивное программирование
Говоря простыми словами, функциональное реактивное программирование можно свести к декларативному реагированию на события или поведение с течением времени. Чтобы понять принципы FRP более подробно, давайте взглянем на формулировку FRP. Затем мы рассмотрим его использование применительно к JavaScript.
Что такое функциональное реактивное программирование?
Более полное определение от родоначальника FRP Конала Эллиота звучит так: функциональное реактивное программирование — денотативное и продолжительное во времени. Эллиот предпочитает описывать эту парадигму программирования как денотативное программирование с продолжительностью во времени, а не «функциональное реактивное программирование».
Функциональное реактивное программирование, в его самом простом, оригинальном определении, имеет два фундаментальных свойства:
Чтобы понять продолжительность во времени, рассмотрим аналогию с использованием векторной графики. Векторная графика имеет бесконечное разрешение. В отличие от растровой графики (дискретное разрешение) векторная графика масштабируется безгранично, она никогда не перерисовывается или становится нечеткой. «Выражения FRP описывают целые эволюции значений во времени, представляя эти эволюции непосредственно как значения первого класса», — Конал Эллиот.
Функциональное реактивное программирование должно быть:
Здесь можно рассмотреть слайды Колала Эллиота о cущности и происхождении FRP. Язык программирования Haskell поддается истинному FRP благодаря своей функциональной, чистой и ленивой природе. Эван Кзаплиски, создатель Elm, дает большой обзор FRP в своем выступлении «Управление временем и пространством: понимание многих подходов FRP».
В самом деле, давайте коротко поговорим об Elm Эвана Кзаплиски. Elm — это функциональный, типизированный язык для создания веб-приложений. Он компилируется в JavaScript, CSS и HTML. Архитектура Elm послужила вдохновением для контейнера состояния Redux приложений JS. Первоначально Elm позиционировался истинным функционально реактивным языком программирования, но начиная с версии 0.17 он реализовывал подписки вместо сигналов в интересах облегчения изучения и использования языка. На этом Elm простился с FRP.
На практике: функциональное реактивное программирование на JavaScript
Традиционное определение FRP может быть трудным для понимания, особенно для разработчиков, не имеющих опыта работы с такими языками, как Haskell или Elm. Однако этот термин чаще всего появляется в интерфейсной экосистеме, поэтому давайте проясним его применение в JavaScript.
Для согласования всего, что вы, возможно, читали о FRP в JS, важно понять, что Rx, Bacon.js, Angular и другие не согласуются с двумя основными принципами определения FRP Конала Эллиота. Эллиот заявляет, что Rx и Bacon.js не являются FRP. Вместо этого они «композиционные системы событий, вдохновленные FRP».
Функциональное реактивное программирование в своей реализации в JavaScript относится к программированию в функциональном стиле при создании и реагировании на потоки. Это довольно далеко от оригинальной формулировки Эллиота (которая специально исключает потоки как компонент), но тем не менее вдохновляется традиционными FRP.
Также очень важно понять, что JavaScript по сути взаимодействует с пользователем и пользовательским интерфейсом, DOM и часто с базой данных. Побочные эффекты и императивный код де факто являются для него стандартом, даже при использовании функционального или функционального реактивного подхода. Без императивного или нечистого кода веб-приложение JS с пользовательским интерфейсом не было бы очень полезным, поскольку оно не могло бы взаимодействовать со своей средой.
Давайте взглянем на пример, чтобы продемонстрировать основные принципы FRP-вдохновленного JavaScript. Этот пример использует RxJS и печатает движения мыши в течение десяти секунд:
Вы можете проверить этот код в действии в JSFiddle: FRP-вдохновленном JavaScript. Запустите скрипт и, пока идет подсчет до 10, наведите указатель мыши в экран с результатом. Вы должны увидеть координаты мыши вместе со счетчиком. Тогда на экран выведется, где была ваша мышь во время каждого 1-секундного интервала времени.
Давайте кратко обсудим эту реализацию шаг за шагом.
В функции addPoint(pointObj) мы выводим в div последние координаты в последнем временном интервале. Это связывает каждый набор координат с соответствующим временным интервалом. Теперь мы можем увидеть, где мышь находилась в конкретный момент времени.
Примечание: эти функции нечистые: у них нет возвращаемого значения и они производят побочные эффекты. Побочные эффекты — манипуляции DOM. Как упоминалось ранее, JavaScript, который мы пишем для наших приложений, часто взаимодействует с областью видимости вне его функций.
Подводя итог: функциональное реактивное программирование
FRP представляет собой написание действий, которые, используя чистые функции, реагируют на события и переводят состояние с предыдущего момента времени к следующему. FRP в реализации JavaScript не придерживается двух основных принципов FRP Конала Эллиота, но в абстрагировании от оригинальной концепции есть определенный смысл. JavaScript сильно зависит от побочных эффектов и императивного программирования, но мы, безусловно, можем использовать преимущества концепций FRP для улучшения нашего JS.
Дополнительную информацию о функциональном реактивном программировании можно найти на следующих ресурсах:
Заключение
Мы закончим еще одной отличной цитатой из первого издания Eloquent JavaScript: «Студент долгое время сидел за своим компьютером, мрачно хмурился и пытался написать красивое решение сложной проблемы, но не мог найти правильный подход. Фу-Цу ударил его по затылку и крикнул: ‘Введите что-нибудь!’. Студент начал писать уродливое решение, и после того, как он закончил, он внезапно понял прекрасное решение».
Понятия, необходимые для понимания функционального программирования, реактивного программирования и функционального реактивного программирования, может быть трудно осознать, не говоря уже о полном овладении. Написание кода, использующего основные принципы какой-либо парадигмы — это первый шаг, даже если он вначале не является полностью верным. Практика освещает путь вперед, а также помогает пересматривать эти концепции.
Используя этот Справочник в качестве отправной точки, вы можете начать использовать представленные концепции и парадигмы программирования для повышения своего уровня владения JavaScript. Если по описанным темам что-либо еще неясно, пожалуйста, обратитесь к ссылкам в каждом разделе за дополнительными ресурсами. Позже мы рассмотрим новые концепции в следующей статье Справочника современных концепций JavaScript!




