Функціональний словник програмування: 25 FP термінів для розробників
Чиста функція, незмінність, функції вищого порядку, монади, каррування і словник функціонального програмування.
Функціональне програмування перейшло від академічної цікавості до загальної практики. Незалежно від того, працюєте ви на TypeScript, Scala, Haskell або навіть пишете гачки React, ви щодня стикаєтеся з концепціями FP. Виклик для багатьох розробників — особливо тих, що працюють в міжнародних командах — полягає в тому, що функціональне програмування має свій власний щільний словник. Нерозуміння одного терміну, наприклад, «монада» або «референтна прозорість» може зробити перегляд коду схожим на іспит з іноземної мови. Цей посібник містить 15 основних термінів з FP з визначеннями простою англійською мовою і прикладами реальних розмов, щоб ви могли безпечно брати участь у технічних обговореннях.
Основні праці: «Функціональна логіка
Ці терміни з’являються майже в кожній обговоренні FP. Якщо нічого іншого не навчишся, то навчись цих.
** Чиста функція ** — функція, яка завжди повертає той самий вивід для того ж вводу і не спричиняє ніяких спостережуваних змін поза собою. Без мережевих викликів, записів файлів, мутацій.
“Ми повинні переробити цей рівень обслуговування. Більшість з цих функцій не є чистими — вони читаються з глобального конфігурації об’єкта, що робить їх неможливим перевірити в ізоляції»
“Я переписав розрахунок ціни як чисту функцію. Тепер тести блоків працюють в мілісекундах і нам не потрібно нічого насміхатися»
** Побічний ефект ** — будь- яка спостережувана взаємодія функції з зовнішнім світом: запис до бази даних, реєстрація у консолі, зміна змінної за межами її обсягу або виконання запиту HTTP.
«Вага була викликана побічною дією в мапері — він безшумно мутував вхідний масив замість того, щоб повернути новий.»
«У нашому FP шарі ми намагаємося відштовхнути всі побічні ефекти до країв застосування. Основна логіка бізнесу залишається чистою»
** Незмінність ** — принцип, за якого після створення структури даних її неможливо змінити. Замість зміни об’ єкта, ви створюєте новий об’ єкт з оновленими значеннями.
«Ми перейшли на незмінні структури даних для нашого магазину Redux. Зневадження стало набагато простіше — ми могли відтворити кожен перехід стану»
«Модифікатор
readonlyTypeScript вимагає незмінності під час компіляції, що є хорошим початком, але не глибоко заморожує вкладені об’єкти»
** Посилальна прозорість ** — вираз є посилально прозорим, якщо його можна замінити на його оцінюваний результат без зміни поведінки програми. Чистий виклик функції є посиланням прозорим; функція, яка читає поточний час, не є.
“Якщо функція є посилально прозорою, ви можете безпечно її замінити. Результат завжди однаковий для тих же аргументів.»
«Ця функція не є посилально прозорою — вона залежить від
Date.now(), тому викликаючи її двічі з тим же вхідним значенням, отримуємо різні виходи»
Функціонує як громадянин першого класу
Однією з ключових ідей функціонального програмування є обробка функцій так само, як ви обробляєте дані — передаючи їх, зберігаючи, повертаючи.
** Функція першого класу ** — функція, яку можна призначити змінній, передати як аргумент або повернути з іншої функції, так само як і будь- яке інше значення.
JavaScript має функції першого класу, саме тому зворотні виклики і функції вищого порядку відчувають себе природно в мові
** Функція вищого порядку ** — функція, яка приймає одну або більше функцій як аргументи, повертає функцію або обидві.
«Наша
withRetryкорисність є функцією вищого порядку. Ви передаєте йому будь-яку асинхронну функцію і він обгортає її з логікою повторних спроб»
«Якщо ви звикнете до функцій вищого порядку, шаблони, такі як середнє програмне забезпечення і декоратори, починають відчуватися очевидними»
** map / filter / reduce ** — три фундаментальні функції вищого порядку. map перетворює кожен елемент збірки, filter вибирає елементи, що відповідають предикату, і reduce (також званий fold ) згортає збірку до одного накопиченого значення.
«Замініть цю петлю
forпетлеюmapіfilter— це буде в половину рядків і набагато більш читабельно»
“Я використовував
reduceдля побудови пошукового об’єкта з масиву. Один прохід, ніяких побічних ефектів»
** Currying ** — перетворення функції, яка приймає декілька аргументів, у послідовність функцій, кожна з яких приймає один аргумент. Названо на честь математика Гаскела Каррі.
“Функція
addє каррованим, томуadd(2)повертає нову функцію, що очікує на другий аргумент. Ви можете частково спеціалізуватися на цьому»
«Каррі — це механізм; часткове застосування — це випадок використання. Вони пов’язані, але не одна і та ж річ»
** Часткове застосування ** — встановлення кількості аргументів до функції, що створює нову функцію з меншою арітією. На відміну від каррування, вона не вимагає зменшення до одноаргументних функцій.
«Ми використовуємо
partialз Ramda, щоб випікати базовий URL в наш API helper. Кожна функція маршруту тоді потребує тільки шляху»
“Часткове застосування чудово підходить для налаштування. Ви встановите інваріантні параметри один раз і повторно використовувати спеціалізовану функцію всюди»
Об’єднання і абстрагування: композиція і шаблони
** Складання функцій ** — поєднання двох або більше функцій так, щоб вивід однієї з них став вхідною інформацією для наступної. Математично записується як f ∘ g, що означає «застосувати g спочатку, а потім f».
«Замість вкладення функції виклику п’ять рівнів глибини, використовуйте
composeабоpipeдля читання перетворення як лінійного конвеєра»
«Композиція функцій є відповіддю FP на спадковість. Збудуйте складну поведінку з маленьких, повторно використовуваних частин»
** Безточковий стиль ** — написання визначення функції без явного згадування аргументів (« точок »). Функція визначається чисто з точки зору композиції і функцій вищого порядку.
«Ця лямбда може бути написана без точок: замість
x => toUpperCase(x)просто передайтеtoUpperCaseбезпосередньо»
«Стиль без точок елегантний, але може пошкодити читабельність, якщо надмірно використовувати. Упевніться, що команда з ним комфортно, перш ніж йти на все»
** Хвостова рекурсія ** — форма рекурсії, у якій рекурсивний виклик є останньою операцією у функції. Хвостова рекурсивна функція може бути оптимізована за часом виконання в петлю, уникаючи переповнення стека для великих вхідних даних.
“Наївна рекурсивна версія розриває стек на великі списки. Переписати його як хвостову рекурсію і оптимізатор перетворить його в ітерацію»
JavaScript не оптимізує хвостові виклики в більшості рушіїв, тому на практиці ми використовуємо трамплін або просто ітерацію
** Легке оцінювання** — відкладання обчислення значення до моменту, коли воно буде дійсно потрібне. Це контрастує з охочим (суворим) оцінюванням, яке обчислює значення негайно.
“З лінивим оцінюванням, ми можемо представляти нескінченні послідовності. Ми обчислюємо тільки стільки елементів, скільки споживач фактично запитує»
“RxJS Observables за замовчуванням є лінивим — нічого не запускається, поки ви не підпишетеся. Це ключова відмінність від Promises»
Основні види: монети та монети
Ці терміни походять з теорії категорій і лякають багатьох розробників. Концепції насправді простіші, ніж їхні назви.
** Функтор ** — будь- яка структура, яку можна перенести. На практиці, функтор є контейнером, який реалізує метод map, який підкоряється двом законам: ідентичності і композиції. Масиви в JavaScript є функторами.
«Масив є найпростішим функтором.
mapзастосовує функцію до кожного значення, зберігаючи структуру контейнера.»
«Ми написали тип
Result, який є функтором. Ви можете безпечно переписувати його — якщо він містить помилку, переписування не є операцією»
Monad — функтор з двома додатковими операціями: of (обгортання значення) і flatMap / chain (відображення з функцією, яка сама повертає обгорнуте значення, а потім сплощення). Монади нададуть вам змогу послідовно виконувати обчислення, які потребують контексту — нульовість, помилки, асинхронність.
Три найкорисніші монади для розробників веб- програм:
- ** Можливо (Параметри) ** — позначає значення, яке може бути відсутнім. Виключає перевірки нульових значень, роблячи відсутність значення явним у типі.
«Ми обгорнули наші нульові результати DB в
Maybe. Тепер весь конвеєр є нуль-безпечним без жодної перевіркиif»
- ** Або** — позначає обчислення, яке може закінчитися успіхом (праворуч) або невдачею (ліворуч). Дозволяє обробку помилок без винятків.
“Замість того, щоб кинути, перевірка повертає
Either. Викликач можеfoldнад ним, щоб обробляти обидві гілки»
- IO — обгортає обчислення, що виконуються з боку, відкладаючи його і роблячи явним у підписі типу, що функція розмовляє з зовнішнім світом.
“Монада
IOробить побічні ефекти цінностями першого класу. Ви можете складати IO дії без їхнього запуску»
Як використовувати їх у розмові
** Перегляд коду: ** “Ця функція не чиста — вона мутує об’ єкт user, який передається. Чи можемо ми повернути новий об’єкт замість цього і зберегти наші дані незмінними по всьому конвеєру?»
** Обговорення архітектури: ** “Проблема в тому, що побічні ефекти розкидані повсюди. Якщо ми підштовхнемо їх до межі і збережемо логіку домену референтно прозорою, ми зможемо перевірити кожне бізнес-правило без насмішок над інфраструктурою»
Приєднання до команди: “Уявіть монаду як коробку з контрактом. Коробка Maybe каже: «Я можу бути порожній, але ви все ще можете безпечно відобразити на мені.’ Ви ніколи не повинні перевіряти на нуль — коробка обробляє це»
** Коментар до запиту на витяг: ** “Нічого не скажеш про каррі. Одна пропозиція: складений конвеєр на рядку 47 читає справа наліво, тому що ви використовували compose. Розглянемо pipe замість цього, щоб він читав в порядку, в якому дані протікають через нього»
Краткий справочник
| Term | One-line definition |
|---|---|
| Pure function | Same input → same output, no side effects |
| Side effect | Any interaction with the outside world |
| Immutability | Data cannot be changed after creation |
| Referential transparency | Expression can be replaced by its value safely |
| Higher-order function | Takes or returns a function |
| Currying | Multi-argument function → chain of single-argument functions |
| Partial application | Pre-fill some arguments, return a specialised function |
| Function composition | Chain functions so output of one feeds input of the next |
| Functor | Container that supports map |
| Monad | Functor that supports flatMap for sequencing contextual computations |