Вибачте за ще одне питання щодо FP + побічних ефектів, але я не зміг знайти існуючого, який цілком відповів би на мене.
Моє (обмежене) розуміння функціонального програмування полягає в тому, що стан / побічні ефекти повинні бути мінімізовані і триматися окремо від логіки без стану.
Я також збираю підхід Хаскелла до цього, монада IO, досягає цього шляхом загортання стаціонарних дій у контейнер для подальшого виконання, розглянутих поза межами самої програми.
Я намагаюся зрозуміти цю закономірність, але насправді, щоб визначити, чи потрібно використовувати її в проекті Python, тому хочу уникати особливостей Haskell, якщо можливо.
Сирий приклад вхід.
Якщо моя програма перетворює XML-файл у файл JSON:
def main():
xml_data = read_file('input.xml') # impure
json_data = convert(xml_data) # pure
write_file('output.json', json_data) # impure
Чи не ефективний підхід МО монад для цього:
steps = list(
read_file,
convert,
write_file,
)
потім звільнити себе від відповідальності, фактично не називаючи цих кроків, а дозволяючи перекладачеві робити це?
Або по-іншому, це як писати:
def main(): # pure
def inner(): # impure
xml_data = read_file('input.xml')
json_data = convert(xml_data)
write_file('output.json', json_data)
return inner
то очікуєте, що хтось інший зателефонує inner()
та скаже, що ваша робота виконана, оскільки main()
це чисто.
У цілому програма, в основному, міститься в монаді IO, в основному.
Коли код фактично виконується , все після прочитання файлу залежить від стану цього файлу, тому все одно страждатимуть від тих самих помилок, що стосуються стану, як і імперативна реалізація, тож ви насправді щось отримали, як програміст, який це підтримуватиме?
Я цілком вдячний на користь скорочення та ізоляції поведінки, що склалася, тому я фактично побудував імперативну версію на зразок такої: збирайте матеріали, чисті речі, випивайте результати. Сподіваємось, ви convert()
можете бути абсолютно чистими і скористатися перевагами керованості, безпекою ниток тощо.
Я також ціную, що монадичні типи можуть бути корисними, особливо в трубопроводах, що працюють на порівнянних типах, але не розумію, чому IO слід використовувати монади, якщо вони вже не знаходяться в такому трубопроводі.
Чи є якась додаткова користь для боротьби з побічними ефектами, яку приносить модель МО монади, якої я не вистачає?
main
програми Haskell IO ()
- це дія вводу-виводу. Це насправді зовсім не функція; це цінність . Уся ваша програма - це чисте значення, що містить вказівки, які повідомляють мові виконання, що вона повинна робити. Всі нечисті речі (фактично виконуючи дії IO) виходять за рамки вашої програми.
read_file
) і використовуєте його як аргумент до наступного ( write_file
). Якби у вас була лише послідовність незалежних дій, вам не знадобиться монада.