Функціональне програмування на PHP. Хабрахабр

17.08.2015

Функціональне програмування на PHP переклад

PHP завжди був простим процедурним мовою програмування, черпавшим своє натхнення з C і Perl. В PHP 5 з’явилася правильна об’єктна модель, але про неї ви вже все знаєте. А ось в PHP 5.3 з’явилися замикання (closure), які були серйозно поліпшені версії 5.4 (підказка: $this тепер доступний за замовчуванням).

Що ж це все-таки таке — функціональне програмування?

Минуло вже кілька років відтоді, як я почав використовувати функціональні елементи у своєму вихідному коді, але я все ще не готовий дати прямого і точної відповіді на це питання. І все ж, незважаючи на те, що у мене поки що немає чіткого визначення – я з упевненістю можу сказати, коли переді мною приклад функціонального програмування, а коли — ні. Тому я спробую зайти трохи з іншого боку: функціональні програми зазвичай не вдаються до зміни станів, а використовують чисті функції. Ці чисті функції приймають значення і повертають значення без зміни свого вхідного аргументу. Протилежний приклад – це типовий сетер в об’єктно-орієнтованому контексті.

Типовий функціональний мова програмування підтримує також функції високого порядку – це функції, які приймають у якості аргументів чи повертають інші функції. Більшість з них підтримує такі речі, як карринг (currying ) і часткове застосування функції (partial function application ). Також у мовах функціонального програмування можна зустріти ретельно продуману систему типів, які використовують type option для запобігання появи нульових покажчиків, які стали звичайною справою для імперативних або об’єктно-орієнтованих мов програмування.

Функціональне програмування володіє кількома спокусливими властивостями: відсутність метушні з станами робить паралелізм простіше (але не простим – паралелізм ніколи не буває простим), фокусування на функції — на мінімальній одиниці коду, який можна було б використовувати знову – може привести до цікавих речей, пов’язаних з їх повторним використанням; вимога до функцій бути певними це відмінна ідея для створення стабільних програм.

Що може запропонувати PHP?

PHP не є «справжнім» або «чистим» функціональним мовою. Він далекий від цього. Тут немає належної системи типів, «круті пацани » катаються зі сміху від нашого екзотичного синтаксису для замикань, а ще тут є функція array_walk(). яка на перший погляд виглядає функціональної, але дозволяє зміна станів.

Тим не менш, тут є кілька цікавих «будівельних блоків» для цілей функціонального програмування. Для початку візьмемо call_user_func. call_user_func_array і $callable(). call_user_func приймає callback-функції і список аргументів, після чого викликає цей callback з переданими аргументами. call_user_func_array робить те ж саме, за винятком того, що вона приймає масив аргументів. Це дуже схоже на fn.call() і fn.apply() в JavaScript (без передачі області видимості). Набагато менше відома, але відмінна функція в PHP 5.4 це можливість викликати функції. callable це мета-тип в PHP (тобто складається з декількох вкладених типів): callable може бути рядком для виклику простих функцій, масивом з для виклику статичних методів і масивом з для виклику методів об’єкта, екземпляра Closure або чого завгодно, здійснює магічний метод __invoke(). також відомий як Функтор. Це виглядає приблизно наступним чином:

В PHP 5.4 з’явився новий тип «callable», який дозволяє легкий доступ до мета-типу callable.

PHP в тому числі підтримує анонімні функції. Як згадувалося раніше, співтовариство Haskell від душі сміється над цим фактом, але головного все одно не відняти — ми нарешті-то їх отримали. Та й жарти були цілком очікувані, тому що синтаксис виразів став дуже важким. Візьмемо простий приклад на Python.

Симпатично, тепер поглянемо на той самий код для Ruby:

Теж непогано, хоч нам і довелося використовувати блок і нестроге лямбда-вираз. У Ruby теж є лямбда-виразу, але List.maphappens приймає блок, а не функцію. Перейдемо до Scala:

Як видно з прикладів, для строго типізованого мови програмування синтаксис завжди залишається досить компактний. Перепишемо наш приклад на PHP:

Ключове слово function і відсутність неявного return змушують код виглядати трохи громіздким. Але, тим не менш, він працює. Ще один «будівельний блок» в скарбничку для функціонального програмування.

до Речі, array_map дає непоганий старт, але варто врахувати, що є ще і array_reduce ; ось вам ще дві важливі функції.

Функціональний приклад з реального світу

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

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

Давайте почнемо з використання функцій вищого порядку:

Тепер зміни станів не відбувається, навіть всередині самої функції. array_map() повертає новий масив зі списку позицій у корзині з вагою, податком та вартістю, а функція array_reduce збирає разом масив підсумкової суми. Чи можемо ми піти далі? Чи можемо ми зробити програму ще простіше?

А що, якщо ми розіб’ємо програму на частини ще менше і подивимося, що вона робить насправді:

  • Підсумовує елемент масиву, помножений на інший елемент
  • Забирає частину відсотків від цієї суми
  • Вважає різницю між процентами та сумою

Тепер нам потрібно маленький помічник. Цим маленьким помічником нам стане functional-php. невелика бібліотека функціональних примітивів, яку я розробляю вже кілька років. Для початку, тут є Functionalpluck(). яка робить те ж саме, що і _.pluck() з underscore.js. Інша корисна функція звідти — це Functionalzip(). Вона «стискає» разом два списки, опціонально використовуючи callback-функції. Functionalsum() додає елементи списку.

Відразу виникає відмінний контраргумент: чи правда, що приклад став простіше для читання? З першого погляду — безумовно ні, але з другого і далі — ви звикнете. Особисто у мене пішло якийсь час на те, щоб звикнути до синтаксису Scala; скільки часу зайняло вивчення ООП і ще чимало пішло на розуміння функціонального програмування. Це найдосконаліша форма, в яку можна перетворити вихідний приклад? Немає. Але за допомогою цього коду ви побачили, наскільки сильно змінюється ваш підхід до нього, коли ви мислите в рамках застосування функцій до структур даних, а не використання виразів на кшталт foreach для обробки структур даних.

Що ще можна зробити?

Ви коли-небудь стикалися з винятками нульового покажчика (null pointer exceptions )? Існує така річ, як php-option. яка надає нам реалізацію полиморфического типу «можливо» (maybe) за допомогою PHP-об’єкта.

Цього є часткове застосування: вона перетворює функцію, яка приймає n параметрів у функцію, яка приймає

Нудний шлях:

Функціональний шлях без PFA (часткового застосування функцій):

Шлях з PFA і з використанням reactphp/curry (моя улюблена реалізація карринга для PHP):

Так. … (HORIZONTAL ELLIPSIS, U+2026) це коректне ім’я функції в PHP. Але якщо воно вам з якоїсь причини не сильно сподобалося, можете використовувати замість нього useCurryplaceholder() .

І наостанок: почитайте «Функціональне програмування в реальному світі » (Real World Functional Programming ). Там повним-повно хороших порад і прикладів використання на практиці.

Чекаю ваших зауважень і поправок до статті в особистих повідомленнях.

Короткий опис статті: php програмування PHP завжди був простим процедурним мовою програмування, черпавшим своє натхнення з C і Perl. В PHP 5 з’явилася правильна об’єктна модель, але про неї ви вже все знаєте. А ось в PHP 5.3… php, функціональне програмування

Джерело: Функціональне програмування на PHP / Хабрахабр

Також ви можете прочитати