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

Сначала я представлю основные концепции в независимой от языка манере, чтобы концепции понимались, не теряясь в синтаксисе и API, специфичных для языка.

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

Подождите... а что такое функциональный стиль? Декларативная парадигма программирования — стиль построения структуры и элементов компьютерных программ, выражающий логику вычислений без описания потока управления.[1]

Я знаю, что это немного, чтобы проглотить с первого раза. Я могу понять.

Итак, давайте попробуем еще раз… Функциональное программирование — это небольшой сдвиг в стиле мышления, и если вы использовали электронные таблицы, то вы уже знакомы с функциональным образом мышления. Ниже приведен пример… достаточно просто… верно?

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

Прежде чем двигаться дальше, давайте разберемся, что мы имеем в виду, говоря: «Функции являются первостепенными в функциональном программировании».По сути, мы говоря, что мы будем относиться к функциям так же, как мы относимся к объектам в ООП, то есть мы можем передать одну функцию другой функции, функция может вернуть другую функцию. Простой!!

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

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

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

Допустим, у нас есть Студент в качестве Объекта предметной области, и у нас есть первая постановка задачи — «данный список студентов, вывести общее количество студентов-первокурсников»

Псевдокод будет выглядеть примерно так:

И когда вы обратите пристальное внимание на приведенный выше псевдокод, вы поймете, что условие «если-иначе» и «цикл for» исчезли, то есть они ушли из словаря языка, но они все еще там с механизмом выполнения.

Лямбда → Условие для функции фильтра
Блок If → Функция фильтра
Блок цикла → Функция сопоставления
Накопить результаты → Функция Fold/Reduce (т. е. сумма в приведенном выше псевдокоде)

Теперь вы спросите, какую пользу я получаю, используя описанный выше код?

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

Скажем теперь, к нашей постановке задачи добавлены дополнительные требования: (a) Распечатать всех студентов-первокурсников (b) Распечатать всех студентов, зачисленных более чем на 3 курса (c) Распечатать всех студентов, у которых остаток на студенческом билете превышает 20 долларов (d) Распечатайте всех студентов, у которых на студенческом билете больше 20 долларов, и которые являются первокурсниками… и так далее, и тому подобное.

Вы согласитесь, что подход «если-иначе» скоро станет уродливым для кода, сложным в обслуживании и тестировании. Вот где Lambda пригодится.

Lambda — это хороший способ передать поведение, где Lambda — это не что иное, как анонимный внутренний класс, который может реализовать любой интерфейс, если есть только одно определение абстрактного метода. Тип лямбда-выражения должен определяться интерфейсом. Такие интерфейсы аннотируются @FunctionalInterface в Java.

См. ниже, чтобы увидеть, как мы переходим от реализации класса Concrete → Anonymous → синтаксис Lambda.

Наш интерфейс Criterion выше — это не что иное, как интерфейс Predicate‹T›, определенный в java.util.function. Увидь себя -

Имея в виду эту информацию, давайте разберемся с концепцией Closure (ага..)

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

Java достигает этого, копируя переменную в объект функции, созданный из шаблона функции, поэтому Lambda в Java принимает переменные, которые являются конечными или фактически конечными.

Следующий; Чистая функция, функция является чистой, если
а. Не имеет побочных эффектов
b. Не использует и не изменяет какие-либо внешние переменные, за исключением использования значений, предоставленных как часть параметра.

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

Иными словами, чистая функция — это функция, в которой возвращаемое значение результата этой функции зависит только от ее аргументов (нет вариаций и нет побочных эффектов). Но тогда какое нам дело?

1. Ссылочная прозрачность
2. Более надежный код и менее подверженный ошибкам (поскольку нет побочных эффектов)
3. Более простое тестирование и отладка
4. Нет необходимости в имитациях
5 , Производительность (кэшировать дорогостоящие операции — см. Currying далее в этой статье)
6. Встроенная поддержка более высокого уровня параллелизма
7. Идемпотентность — означает, что вы можете выполнять их несколько раз ( особенно полезно в распределенной системе)

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

Шаблон Monad состоит из трех основных частей:
1. Способ переноса данных в монаду
2. Способ извлечения исходных данных
3. FlatMap Метод — требует, чтобы вызывающая сторона обеспечивала поведение, которое берет один элемент, который в настоящее время заключен в монаду, и в результате возвращает другую монаду.

Думайте о Monad как об аналоге Wrapper, который достаточно хорош для понимания, не вдаваясь в математическую строгость.

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

Вся идея Pure Function, Monad вместе с Pipeline на самом деле является очень аккуратной и гибкой идеей, которая является основой Stream API в Java.

Помимо Stream, Optional в пакете java.util является еще одним примером монады (полезно избегать проверок Null). С необязательным мы просто говорим, что должно произойти, и это произойдет, если есть доступные данные, а если нет никаких данных, это незаметно не произойдет. Но нам не нужно постоянно проверять нулевые указатели.

Ниже приведен пример монады, где SuperIterable предоставляет возможность применять операции и связывать их.

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

Последняя концепция, которую вы хотите освоить, — это «Каррирование», названное в честь американского математика и логика Хаскелла Брукса Карри, техника, используемая для преобразования функций в математике и информатике.

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

Теперь следующий вопрос: зачем вам вообще заниматься каррированием?

Оказывается, то, что было нетерпеливой операцией → x + y, теперь можно выполнять лениво, а это означает, что мы можем делать как минимум две вещи
1. Кэшировать дорогостоящие операции
2. Достигать абстракций в функциональной парадигме.

Поскольку эта статья становится все больше, я подробно расскажу о карринге, потоках и т. д. в следующей статье. А пока я позволю вам исследовать его самостоятельно.

Удачного кодирования!! Приятного обмена !!

Ссылки:
[1] https://en.wikipedia.org/wiki/Функциональное_программирование
[2] https://en.wikipedia.org/wiki/Декларативное_программирование
[3] Барри Берд и Саймон Робертс
[4] Тема на stackoverflow — Монада на простом английском? (Для программиста ООП без фона FP)
https://stackoverflow.com/questions/2704652/monad-in-plain-english-for-the-oop-programmer-with-no-fp-background

Ниже приведены некоторые интересные статьи о функторах, аппликативах, монадах и т. д.
[1] Функторы, аппликативы и монады в картинках
https://adit.io/posts/2013-04-17-functors ,_applicatives,_and_monads_in_pictures.html#functors
[2] Функторы и монады для тех, кто прочитал слишком много учебников
http://www.jerf.org/iri/post/2958
[3] Понимание функтора и монады с мешком арахиса
https://medium.com/beingprofessional/understanding-functor-and-monad-with-a-bag-of-peanuts-8fa702b3f69e
[4] Ваше простое руководство по монадам, аппликативам и функторам
https://medium.com/@lettier/your-easy-guide-to-monads-applicatives-functors-862048d61610
[5]Монады и функторы: функциональное программирование
https://faun.pub/functional-programming-2193de21ec51