Функциональное программирование: рефакторинг, замыкания и функции высшего порядка

0
2638

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

Рефакторинг

Взгляните на пример кода на JavaScript:

Две эти функции практически идентичны. Вместо копирования и изменения validateSsn, давайте попробуем объединить функции в одну, а различия вынести в переменные. Введем значения value, regular expression и message:

Параметры ssn и phone представлены в виде value. Регулярные выражения /^\d{3}-\d{2}-\d{4}$/ и /^\(\d{3}\)\d{3}-\d{4}$/ вынесены в regex. Части сообщения, отвечающие за вид проверки вынесены в type.

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

Но случается, что вы видите имеете такой код:

Здесь parseAddress и parseFullName – функции, которые принимают строку и возвращают true, если она разбирается. По аналогии с прошлым примером, мы можем ввести value для address и name, и type для ‘Address’ или ‘Name’. Но как передать в параметре функцию?

Функции высшего порядка

Многие языки программирования не поддерживают передачу функций в качестве параметра. В функциональном программировании, функция может передаваться в другую функцию наравне с переменными.

Хотя JavaScript и не является чистым функциональным языком, в нем можно проделывать некоторые функциональные трюки, в том числе, передавать функцию в качестве параметра.

Для нашего примера введем параметр parseFunc, в который будем передавать функции для обработки строк.

То, что мы получили, называется функцией высшего порядка.

Теперь, мы можем вызывать новую функцию высшего порядка для каждого из четырех примеров функций выше, потому что в JavaScript Regex.exec будет также возвращать true, если строка пройдет проверку:

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

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

Так, мы создали функцию, которая принимает регулярное выражение и возвращает функцию для парсинга с .exec на конце.

Функция, как результат работы функции

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

Вот еще один пример такой функции:

Здесь функция makeAdder принимает constantValue и возвращает adder, функцию, которая прибавляет константу к любому значению, которое получает. Вот как можно это использовать:

Мы создали функцию add10, передав константу 10 функции makeAdder, которая вернет значение + 10 от любого исходного.

Обратите внимание, что adder имеет доступ к constantValue даже после того, как makeAddr вернула значение. Так происходит потому, что constantValue была в своей области, когда adder была создана.

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

Это называется «замыкания».

Замыкания

Ниже показан выдуманный пример функции, которая использует замыкания.

В этом примере child имеет доступ к своим переменным, переменным родителя и переменным родителя родителя.

parent имеет доступ к своим переменным и переменным своего родителя.

grandParent имеет доступ только к своим переменным.

Так это можно использовать:

Здесь parentFunc удерживает область parent с того момента, как grandParent возвращает parent.

Также, childFunc удерживает область child с того момента, как parentFunc, которая просто parent, возвращает child.

Когда функция создана, все переменные в ее области во время создания доступны на время жизни функции. Функция существует столько, сколько существуют ссылки на нее. Например, область функции child существует, пока на нее ссылается childFunc.

Замыкания – полезный инструмент, но в JavaScript он может приносить проблемы, когда переменные меняют значения. К счастью, в функциональном программировании переменные значения не меняют.