Функциональное мышление в Swift

Когда я впервые учился программированию, меня учили объектно-ориентированному программированию, и поэтому я научился думать о программном обеспечении с объектно-ориентированным мышлением. Однажды я смотрел интервью с Аланом Каем на YouTube, в котором он сделал заявление:

Лисп - самая важная идея в информатике.

Конечно, на этом этапе я должен был понять, почему он сказал такие вещи. Изучая Lisp, я наткнулся на функциональное программирование. Я слышал этот термин раньше, но никогда не понимал его. В конце концов, определение функционального программирования в Википедии гласит:

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

Хорошо, это другая парадигма, чем объектно-ориентированное программирование. Но математические функции? Нужно ли мне изучать теорию категорий, чтобы использовать функциональное программирование? Как применить математические функции к объектам модели и контроллерам представлений?

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

Есть много интересных причин изучать функциональное программирование. Из их:

  • Функциональное программирование помогает уменьшить количество состояний в нашем коде
  • Это делает наш код более тестируемым и его легче рассуждать.
  • Это делает наш код более компонуемым

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

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

Примечание. Swift 4.0 представляет протокол Codable для облегчения синтаксического анализа JSON. Это все еще поучительный пример, поскольку анализ JSON хорошо знаком многим разработчикам мобильных приложений.

Пример из реального мира: разбор ответа JSON от сервера

Как показано на диаграмме выше, для синтаксического анализа ответа JSON от сервера требуется два преобразования:

  1. Из необработанного HTTP-ответа в JSON-представление данных.
  2. От представления JSON к модели.

Между первым и вторым шагами мы будем работать с JSON-представлением данных. Это представление JSON позволяет нам выполнять инкрементные преобразования ответа от сервера. Анализируя промежуточное представление JSON перед моделью, наш код становится более компонуемым.

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

Если вы прочитаете документацию для JSONSerialization, вы заметите, что одно из правил для действительного JSON:

Все объекты являются экземплярами NSString, NSNumber, NSArray, NSDictionary или NSNull.

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

Определив тип JSONObject, мы теперь знаем тип наших данных до первого преобразования, между первым и вторым преобразованиями и после второго преобразования. Это Data - ›JSONObject -› Model (мы определим модель немного позже.)

Функциональный подход

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

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

Преобразование 1: данные в объект JSON

Мы начнем с определения нового типа Deserialize, который преобразует Data в JSONObject. В функциональном программировании типы определяются по сигнатуре их метода. Чтобы использовать этот тип, мы пишем функцию, которая возвращает наш новый тип Deserialize.

Я хотел бы выделить несколько моментов в нашей JSON() функции.

  1. Функция возвращает закрытие. Взгляд на это с объектно-ориентированным мышлением может показаться странным, но это стандартное функциональное программирование.
  2. Обратите внимание, что замыкание, возвращаемое из JSON(), полностью не зависит от любого состояния, которое может существовать вне функции. Это резкое сравнение с объектно-ориентированным программированием, где мы так зависим от состояния.

Преобразование 2: объект JSON в модель

Как и в первом преобразовании, давайте определим новый тип, который принимает JSONObject в качестве параметра и возвращает модель типа T:

typealias Decode<T> = (JSONObject?) -> (T?)

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

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

Собираем все вместе

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

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

let userJSON: [String: Any] = [
    “FirstName”: “Tyler”,
    “AvatarURL”: “https://upload.wikimedia.org/wikipedia/commons/thumb/3/3c/William_Howard_Taft_1909b.jpg/1200px-William_Howard_Taft_1909b.jpg"
]
let userData = try JSONSerialization.data(withJSONObject: userJSON, options: [])
let user = decodeUser()(JSON()(userData))
print("\(user!.firstName)")

Сравнение и противопоставление объектно-ориентированному подходу

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

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

Плюсы

  • Наш функциональный код полностью неизменяем - от нас не требуется поддерживать какие-либо ссылки или состояния в наших функциях. Это упрощает понимание нашего кода.
  • Функциональный код чистый - при одном и том же вводе каждый раз он дает один и тот же результат.
  • Чистые функции делают наш код более тестируемым.
  • Swift поощряет использование функционального программирования. В язык уже встроено множество концепций функционального программирования. Такие вещи, как optionals, map и reduce - все это функциональные концепции, так почему же не изучать функциональное программирование ?!
  • Это более компонуемо. Разбив логические фрагменты кода на отдельные функции, мы можем начать объединять несколько функций в цепочку для достижения определенного уровня детализации, который необходим нашим программам.
  • Меньше строк кода. Наш функциональный подход к этой проблеме включает примерно 50 строк кода. Между тем объектно-ориентированный подход состоял примерно из 80 строк кода.

Минусы

  • Код трудно читать через объектно-ориентированную линзу. Концепции и конструкции функционального программирования могут быть трудными для понимания на начальном этапе.
  • Синтаксис выглядит странно. В функциональном программировании именование имеет больше смысла, если вы используете глаголы. Это нелогично, если вы пришли из объектно-ориентированного фона, где существительные обычно используются для именования. Например:
// Using verbs
let user = decodeUser()(JSON()(userData))
// vs. using nouns
let user = userDecoder()(JSONDecoder()(userData))
  • Странно выглядит синтаксис, часть II. Серьезно, я серьезно, что за дело с лишними круглыми скобками?

Дополнительные ресурсы

Игровая площадка Swift с приведенным выше кодом доступна здесь.

Если вы хотите узнать больше о функциональном программировании, вот несколько ресурсов, которые я нашел особенно интересными / информативными.

- https://purelyfunctional.tv

- https://fsharpforfunandprofit.com/video/

- https://www.destroyallsoftware.com/talks/boundaries

- http://2017.funswiftconf.com

- http: //λπω.com

- http://learnyouahaskell.com