Читаемость кода упрощает как процесс написания программ, так и последующие действия – от отладки и оптимизации до тестирования и сопровождения.   Один из эффективных способов для понимания кода – применение функциональной парадигмы программирования. Основная идея функционального программирования состоит в представлении процесса вычислений как последовательного изменения состояний без хранения где-либо самих состояний. В качестве примера системы, в которой хорошо реализован функциональный подход, часто приводят Haskell, а также Erlang или Scala. Внедряя такой подход в распространенные языки, такие как JS или Swift, можно добиться как улучшения читаемости, так и тестируемости. Конечно, нужна некоторая предварительная практика, без которой чтение кода оказывается занятием трудоемким, но после знакомства с основными элементами функционального программирования все становится заметно проще. Нужно понимание, что такое функтор, моноид и монада. Дадим определения каждому из базовых понятий, пусть не строгие, но достаточные для практического применения в контексте нашего повествования. Моноид – тип, у которого задано правило комбинирования двух элементов этого типа для получения нового элемента такого же типа. Также существует нейтральный (пустой) элемент, комбинирование с ним другого элемента любого типа дает результат, равный этому другому элементу.  На языке Swift можно представить следующим протоколом: Функтор – метод, позволяющий отобразить один тип моноида в другой. Это знакомый всем, простой и понятный метод map.  Таким именем и назовем протокол, реализованный на Swift. Объединением двух перечисленных элементов является монада – функтор с расширенными возможностями, одним из которых может быть, например, связывание. Для понимания описанных сущностей рассмотрим несколько практических примеров. Всем хорошо известны ошибки при обращении к переменной, которая должна была ссылаться на объект, но содержит пустое значение, что приводит к падению программы.  В данном случае можно воспользоваться монадой Maybe, позволяющей идентифицировать наличие или отсутствие значения в переменной. Зададим способы работы с ней и обезопасим себя от случайного обращения к несуществующему объекту. К примеру, определив оператор связывания (bind) как >>-, принимающий монаду Maybe, и операцию, которую мы хотим выполнить только в том случае, если объект существует: В примере намеренно задан неверный URL адрес. Но если создать URL не получится, то все последующие шаги выполнены не будут, что защитит программу от «вылетания». Комбинируя подобным образом действия, можно сократить, например, вложенность, количество единообразных проверок на наличие непустых значений в каждом вызываемом методе. Рассмотрим еще один пример работы с ошибками. В данном случае при возникновении ошибки создаем объект Result.failure и передаем его в колбэк completion, а в случае успешного получения данных – создаем Result.success и передаем его туда же, в колбэк completion: Заметим, что прямолинейная запись позволяет отказаться от ветвлений и записать все в декларативной форме, что улучшает понимание кода. Аппликативный функтор (<*>) удобен при работе с делегатами или с функциями обратного вызова. В последнем случае мы видим слабую ссылку на функции, в которые надо передать результат выполнения асинхронной операции. Например, комбинируя его с предыдущим примером, можно получить элегантное решение такой задачи: вызвать callback только в том случае, если ссылка на него актуальна и картинка загружена по ссылке, составленной из переданной ранее строки. В JavaScript большую популярность приобрела монада Promise, которая позволяет строить такие же цепочки зависимых операций и отказаться от callback hell в случае асинхронных операций.
Например, с помощью Promise можно реализовать вышеприведенные операции загрузки, обработки, конвертации с последующим вызовом колбэка для совместимости со старой кодовой базой. В случае ошибки, как мы помним, вся цепочка вызовов прерывается без нарушения выполнения программы. Представив выполнение сетевого взаимодействия через Promise (промисы) мы сохранили читаемость и – что важнее! – оперировали понятиями не как делать, а что делать.  Важным результатом от внедрения «функциональщины» является упрощенная тестируемость. В цепочке выполнения можно внедрять функции, поставляющие данные для тестирования поведения (https://en.wikipedia.org/wiki/Mock_object) всей цепочки. Конечно, необходимо соблюдать границы разумного и не увлекаться «функциональщиной» чрезмерно. Перемудрив, мы увеличиваем порог вхождения, как следствие – процесс ввода в курс дела нового специалиста становится сложен и длителен, а поэтому оказывается неприлично дорогим для компании. Проще говоря, избыточное увлечение таким подходом довольно быстро приведет к моменту, когда новые ребята из вашей команды ужаснутся, впервые открыв код, и увидев в нем разные непонятные «смайлики» вперемежку со странными символами. Что делать, чтобы этого не произошло? Соблюдать границы насыщения монадами! Для этого есть простые правила: Юрий Шикин — Uma.Tech