Функціональне програмування включає багато різних методик. Деякі методи прекрасні з побічними ефектами. Але один важливий аспект - екваціональне міркування : Якщо я називаю функцію з однаковим значенням, я завжди отримую однаковий результат. Тому я можу замінити виклик функції повернутим значенням та отримати рівнозначну поведінку. Це полегшує міркування про програму, особливо при налагодженні.
Якщо функція має побічні ефекти, це не зовсім відповідає. Повернене значення не є еквівалентним виклику функції, оскільки значення повернення не містить побічних ефектів.
Рішення полягає в тому, щоб припинити використання побічних ефектів і кодувати ці ефекти у зворотному значенні . Різні мови мають різні системи ефектів. Наприклад, Haskell використовує монади для кодування певних ефектів, таких як мутація IO або State. Мови C / C ++ / Rust мають систему типів, яка може заборонити мутацію деяких значень.
Імперативною мовою print("foo")
функція щось надрукує і нічого не поверне. У чистому функціональному мові, як Haskell, print
функція також приймає об'єкт, що представляє стан зовнішнього світу, і повертає новий об'єкт, що представляє стан після виконання цього виводу. Щось схоже на newState = print "foo" oldState
. Я можу створити стільки нових держав зі старого стану, скільки мені подобається. Однак основна функція коли-небудь використовуватиметься лише одна. Тому мені потрібно послідовно стані з кількох дій, ланцюгуючи функції. Для друку foo bar
я можу сказати щось на кшталт print "bar" (print "foo" originalState)
.
Якщо вихідний стан не використовується, Haskell не виконує дій, що призводять до цього стану, оскільки це ледача мова. І навпаки, ця лінь можлива лише тому, що всі ефекти явно кодуються як зворотні значення.
Зауважте, що Haskell є єдиною часто використовуваною функціональною мовою, яка використовує цей маршрут. Інші функціональні мови в т.ч. сім'я Лісп, сім'я ML та новіші функціональні мови, такі як Scala, відштовхують, але дозволяють все-таки побічні ефекти - їх можна назвати імперативно-функціональними мовами.
Використання побічних ефектів для вводу / виводу, ймовірно, добре. Часто введення / виведення (крім реєстрації даних) робиться лише на зовнішній межі вашої системи. Жодна зовнішня комунікація не відбувається в межах вашої бізнес-логіки. Тоді можна записати ядро вашого програмного забезпечення в чистому стилі, виконуючи при цьому нечисті введення-виведення у зовнішній оболонці. Це також означає, що ядро може бути без громадянства.
Безгромадянство має низку практичних переваг, таких як підвищення розумності та масштабованості. Це дуже популярно для програм із веб-додатків. Будь-який стан зберігається зовні, у спільній базі даних. Це полегшує балансування завантаження: мені не потрібно приклеювати сеанси на певний сервер. Що робити, якщо мені потрібно більше серверів? Просто додайте ще одну, оскільки вона використовує ту саму базу даних. Що робити, якщо один сервер виходить з ладу? Я можу повторити будь-які очікувані запити на іншому сервері. Звичайно, все ще є стан - у базі даних. Але я зробив це явно і витяг, і міг би використовувати внутрішньо чистий функціональний підхід, якщо хочу.