Функциональное программирование на Swift
https://t.me/simbirsoft_dev
Функциональное программирование — это парадигма разработки программного обеспечения, основанная на математических функциях и принципах, которые позволяют создавать более чистый, модульный и надежный код. Данная концепция открывает новые возможности организации кода, делая его более гибким и переиспользуемым, что может значительно уменьшить затраты на разработку, скомпоновать функциональность для повторного использования, сделать целый ряд функций-помощников, упрощающих выполнение программы.
Меня зовут Артём, я iOS-разработчик SimbirSoft, и в этой статье мы рассмотрим подход объединения (композиции) функций, позволяющий объединять различные функции и периодически использовать их в коде. Материал будет полезен iOS-разработчикам уровня Junior и Junior+.
Композиция функций как основа
Наверняка за время изучения программирования вы сталкивались с таким понятием как чистая функция. Давайте вспомним, что это такое:
Чистая функция выполняет два условия. Она всегда возвращает один и тот же результат при одних и тех же входных параметрах. И вычисление результата не вызывает побочных эффектов, связанных с выводом и заимствованием данных во вне.
Это одна из основ функционального программирования. Коротко говоря, ожидаемый результат и нет изменения состояния вовне. Понимание работы чистой функции открывает ключ к совершенно иному способу разработки.
Заметим, что все используемые далее функции, такие как pipe, over, set, map являются чистыми функциями и оперируют generic значениями (<A, B, C>). Если не знакомы с generic-функциями, рекомендую для начала изучить их перед чтением данной статьи.
Давайте рассмотрим наш первый оператор, который и будет оператором композиции.

Данный оператор принимает на вход 2 функции и возвращает новую функцию, которая принимает в себя аргумент первой функции (A) и возвращает результат второй функции (C). Применим его на практике:

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

Функция prop():

Объявление достаточно сложно выглядит, но если вкратце, функция изменяет значение изначального объекта по ключу [keyPath:], который доступен для каждого объекта, имеющего свойства (поля).

- Мы объявляем нашу функцию prop, которая принимает в себя KeyPath, то есть путь к нашему полю surname. Обратим внимание, что тип принимаемого значения функции changeSurname: @escaping (String) -> String, то есть тоже функция.
- Мы объявляем другую функцию, которая использует changeSurname и в качестве параметра передаём функцию { $0.uppecased() }.
- Вызываем функцию makeUppercasedSurname от класса Person и получаем результат.
Функция over():

Функция over позволяет нам сразу объявить функцию от KeyPath и сократить наш код, но такую функцию сложно композировать с другими.
Функция map():

Функция map для Sequence позволяет нам перенести преобразование на каждый элемент коллекции, подобно стандартной функции map языка Swift, только этот map используется при композиции.
Функция with():

Функция, которая применяет значения наших функций к объекту “a”
Функция set():

Функция, которая применяет значение по ключу.
Вернёмся к классу Person. Скажем, что мы хотим добавить каждому ребёнку по фамилии и сделать все надписи большими буквами. Наш код будет выглядеть так:

- Создаём класс Person.
- Берём ранее рассмотренную функцию изменения фамилии.
- Создаём функцию, которая изменяет поле children, а в композиции с map начинает изменять каждого ребёнка.
- Описываем с помощью over, как добавить фамилию ребёнку в массиве children.
- Описываем с помощью over, как сделать заглавными буквами имена и фамилии каждого ребёнка в массиве children.
- Объединяем всё с помощью функции with.
В результате мы получили, что желали: Brown стало заглавными, а дети приобрели фамилии, в которых все буквы заглавные.
Перейдем к более реальному примеру.
Предположим, нам нужно отправить request серверу, и мы хотели использовать принцип композиции.

- Мы объявляем функцию, которая будет гарантировать, что наши allHTTPHeaderFields не будут опциональными на момент обращения к ним. Это нам необходимо для применения к ним функций.
- Описываем функцию, которая будет устанавливать значение заголовка запроса. Обратим внимание, что здесь две функции prop. Одна указывает на поле, которое подлежит изменению, а другая будет создавать по ключу в словаре значение. Как мы помним, при присваивании новому ключу значения в словаре создаётся новый элемент.
- Мы объединяем 2 функции, где первая устанавливает метод, а другая заголовок. Заметим, что здесь мы используем ранее объявленную функцию.
- Объявляем заголовок Accept для github.
- Добавляем токен к запросу.
- Объединяем наш результат посредством функции with и всех ранее описанных функций.
Таким образом мы получили готовый запрос из описанных функций и собрали его подобно конструктору.
Вывод
Мы рассмотрели вершину айсберга функционального программирования, а именно композицию функций, грамотное использование которой может значительно упростить процесс разработки.
Всем рекомендую ознакомиться с разработчиками PointFree, они рассказывают много чего интересного о программировании под iOS. Кто ещё больше хочет погрузиться в функциональное программирование, может рассмотреть язык Haskell, который является чисто функциональным языком. Некоторые из описанных в статье операторов существуют в самом языке, да и сам он послужил основой для создания библиотеки Overture.
А чтобы проверить свои знания о Swift более обширно, предлагаем пройти наш небольшой тест.
Использованные ресурсы:
https://github.com/pointfreeco/swift-overture
https://www.pointfree.co/
https://github.com/haskellcats/haskell-operators
https://swiftbook.ru/content/languageguide/generics-in-swift/
Спасибо за внимание! :)