Функциональное программирование на Swift

Функциональное программирование на Swift

https://t.me/simbirsoft_dev

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

Меня зовут Артём, я iOS-разработчик SimbirSoft, и в этой статье мы рассмотрим подход объединения (композиции) функций, позволяющий объединять различные функции и периодически использовать их в коде. Материал будет полезен iOS-разработчикам уровня Junior и Junior+.

Композиция функций как основа

Наверняка за время изучения программирования вы сталкивались с таким понятием как чистая функция. Давайте вспомним, что это такое:

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

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

Заметим, что все используемые далее функции, такие как pipe, over, set, map являются чистыми функциями и оперируют generic значениями (<A, B, C>). Если не знакомы с generic-функциями, рекомендую для начала изучить их перед чтением данной статьи.

Давайте рассмотрим наш первый оператор, который и будет оператором композиции.

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

  1. Объявим целочисленный массив.
  2. Добавим функцию, удваивающую исходное значение.
  3. Добавим функцию инкремента.
  4. Преобразуем массив путём композиции.
  5. Полученный вывод.

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

Предположим, мы имеем исходную структуру Person:

Функция prop():

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

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

Функция over():

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

Функция map():

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

Функция with():

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

Функция set():

Функция, которая применяет значение по ключу.

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

  1. Создаём класс Person.
  2. Берём ранее рассмотренную функцию изменения фамилии.
  3. Создаём функцию, которая изменяет поле children, а в композиции с map начинает изменять каждого ребёнка.
  4. Описываем с помощью over, как добавить фамилию ребёнку в массиве children.
  5. Описываем с помощью over, как сделать заглавными буквами имена и фамилии каждого ребёнка в массиве children.
  6. Объединяем всё с помощью функции with.

В результате мы получили, что желали: Brown стало заглавными, а дети приобрели фамилии, в которых все буквы заглавные.

Перейдем к более реальному примеру.

Предположим, нам нужно отправить request серверу, и мы хотели использовать принцип композиции.

  1. Мы объявляем функцию, которая будет гарантировать, что наши allHTTPHeaderFields не будут опциональными на момент обращения к ним. Это нам необходимо для применения к ним функций.
  2. Описываем функцию, которая будет устанавливать значение заголовка запроса. Обратим внимание, что здесь две функции prop. Одна указывает на поле, которое подлежит изменению, а другая будет создавать по ключу в словаре значение. Как мы помним, при присваивании новому ключу значения в словаре создаётся новый элемент.
  3. Мы объединяем 2 функции, где первая устанавливает метод, а другая заголовок. Заметим, что здесь мы используем ранее объявленную функцию.
  4. Объявляем заголовок Accept для github.
  5. Добавляем токен к запросу.
  6. Объединяем наш результат посредством функции 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/

Спасибо за внимание! :)


Report Page