Функциональный стиль: объясняю как другу

Читай, наслаждайся, не душни!
Читай, наслаждайся, не душни!

"Функциональное программирование", "Immutable значения", "Pure функции".Тоже слышал эти умные слова?

И ты такой: "Что? Можно просто for написать?"

Спокойно. Сейчас все разложим по полочкам. Даже если ты пишешь var a = 1 и гордишься этим - ты свой. Погнали.

___

Многие думают, что функциональный стиль это просто когда ты используешь map, filter, reduce и чувствуешь себя умным. Типа, "я же не пишу for, значит я уже в functional game". И это…правда. Но только частично.

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

Функциональный стиль (Functional Programming) - такой подход, где:

- функции как блоки Лего: склеиваешь, передаешь, тасуешь;

- данные не меняются (immutable);

- функции честные, без сюрпризов - всегда выдают одно и то же (pure);

- ты не говоришь «делай цикл», а говоришь «вот что я хочу» (declarative).

Выглядит красиво, звучит страшно. По факту - просто удобно.

___

Тезис 1: все через map, filter, reduce

Да-да, вот эти магические штуки:

let numbers = [1, 2, 3, 4, 5] let squares = numbers.map { $0 * $0 } // [1, 4, 9, 16, 25] let evens = numbers.filter { $0 % 2 == 0 } // [2, 4] let sum = numbers.reduce(0, +) // 15

Перевод на человеческий:

- map: обработай каждую штуку;

- filter: убери лишнее;

- reduce: собери все в одну кучу.

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

Меньше циклов, меньше багов, больше кайфа.

___

Тезис 2: let - это любовь

Если ты все еще на var, как на сигаретах - пора бросать.

let name = "Pepe" name = "Froggy" // Ага, получай ошибку

Чем меньше ты меняешь - тем меньше ты ломаешь.

let - твой друг. var - бывший, которого пора отпустить.

___

Тезис 3: Чистые функции (pure) как хороший друг - не подставит

func double(_ x: Int) -> Int { x * 2 }

- всегда честно возвращает одно и то же;

- не печатает в консоль;

- не трогает глобальные переменные.

Просто делает свою работу.

Противоположность:

func randomize(_ x: Int) -> Int { x * Int.random(in: 1...10) }

Это уже мутный тип. Как начальник, который обещал повышение.

___

Тезис 4: Функции как значения (Higher-order functions)

func add(_ a: Int, _ b: Int) -> Int { a + b } let operation: (Int, Int) -> Int = add operation(3, 4) // 7

Функции как карты в колоде. Комбинируй, передавай, твори.

___

Тезис 5: Композиция (composition) - склеивание функций

func double(_ x: Int) -> Int { x * 2 } func increment(_ x: Int) -> Int { x + 1 } let result = { double(increment($0)) } result(3) // 8

Маленькие функции можно собрать в одну большую. Как бутерброд.

Чем проще куски, тем проще их переставлять.

___

Тезис 6: Типы-значения (value types) > ссылочные (reference types)

Когда ты работаешь со struct (а не с class), ты меньше паришься, кто где что поменял. Каждое изменение - новая копия. Никто не ломает твою память.

struct Point { let x: Int let y: Int }

Ты - хозяин объекта, а не какой-то левый ViewController, который взял и поменял тебе «x» в 3 часа ночи.

___

Тезис 7: Опционалы, Result и map - цепочки без боли

В Swift есть типы, которые позволяют строить цепочки операций, но только если все идет хорошо.

let number: Int? = 5 let doubled = number.map { $0 * 2 } // 10?

Если number - nil, функция внутри map даже не вызовется.

То же самое с Result:

enum MyError: Error { case oops } let badResult: Result = .failure(MyError.oops) let squared = badResult.map { $0 * $0 }

А если Result это .failure?Тогда map просто пройдет мимо, не тронув ошибку.

Это как конвейер: если деталь бракованная - просто не обрабатываем, а едем дальше. Никаких истерик.

map, flatMap и другие…позволяют писать код без if let, guard, switch. Они аккуратно работают только с "нормальными" значениями и не трогают плохие.

___

Знаешь, кто реально кайфует от функционального и декларативного подхода?

Твой любимый SwiftUI.

Он буквально кричит:

"Забудь ты свои UIView, забудь свой delegate, просто скажи, что ты хочешь - и я все сделаю".

struct HelloView: View { var body: some View { Text("Привет, мир!") .font(.title) .foregroundColor(.blue) .padding() } }

Никаких layoutSubviews(), addSubview() и прочего душного кода. Ты просто описываешь результат, а не процесс. Вот это и есть декларативный стиль.

body - функция, возвращающая описание интерфейса -> pure.

цепочки .font(.title).foregroundColor(.blue).padding() -> чистейший function chaining.

___

Функциональный стиль это не про "быть модным".

Это про:

- меньше багов;

- меньше головной боли;

- меньше var, for, if let, guard let.

Это как писать код, который сам себя объясняет.

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

Там я делаю тестовое задание на джуна, где надо:

- построить граф;

- пройтись по нему (DFS);

- построить остовное дерево;

- применять паттерны проектирования;

- не забывать про ООП.

В первой части я писал "чтобы работало".

А во второй - рефакторинг. Переписываем все в функциональном стиле, аккуратно, по-человечески.

___

Пример из тестового

На этапе генерации карты нужно было создать массив всех возможных координат комнат.

Типа:

- пройтись по всем y;

- внутри по всем x;

- для каждой пары (x, y) создать Position.

В императивном стиле это выглядело бы так:

for y in 1...mapSize.height { for x in 1...mapSize.width { positions.append(Position(x: x, y: y)) } }

Работает? Да.Красивая ли это жизнь? Не очень.

Теперь функциональная версия:

let positions = (1...mapSize.height).flatMap { y in (1...mapSize.width).map { x in Position(x: x, y: y) } }

flatMap разворачивает двумерную структуру в плоский массив.

map создает Position для каждой координаты.

Все без единого var, append и посторонних слов.

Второй пример

К сожалению, ограничение по количеству символов.

Подробнее в Telegram.

___

Функциональный стиль это не магия, не религия и не то, что нужно учить ночами.Это просто способ писать чище, спокойнее и предсказуемо.

Ты все еще можешь любить классы, ООП и даже иногда писать for.

Но когда попробуешь map, flatMap, pure и immutable, назад будет тяжело.

Это как впервые попробовать нормальный кофе. Вроде то же самое, но жить веселее.

Начать дискуссию