Як побудувати систему, яка має все перелічене нижче :
- Використання чистих функцій з незмінними об'єктами.
- Передавайте лише дані про функції, які потрібні цій функції, не більше (тобто немає великого об'єкта стану програми)
- Уникайте занадто багато аргументів до функцій.
- Уникайте необхідності конструювання нових об'єктів лише для упаковки та розпакування параметрів до функцій, просто щоб уникнути занадто великої кількості параметрів, переданих функціям. Якщо я збираюсь пакувати кілька елементів у функції як один об'єкт, я хочу, щоб цей об'єкт був власником цих даних, а не тим, що створюється тимчасово
Мені здається, що державна монада порушує правило №2, хоча це не очевидно, оскільки воно сплетене через монаду.
У мене є відчуття, що мені потрібно якось користуватися лінзами, але дуже мало написано про це для нефункціональних мов.
Фон
Як вправу я перетворюю одне із своїх існуючих застосунків з об'єктно-орієнтованого стилю у функціональний. Перше, що я намагаюся зробити - це зробити якомога більше внутрішнього ядра програми.
Одне, що я чув, - це те, як керувати "Державою" чисто функціональною мовою, і це, на мою думку, робиться державними монадами, - це логічно, ви називаєте чисту функцію ", переходячи в стан світ таким, яким він є ", тоді, коли функція повертається, вона повертає вам стан світу, як він змінився.
Для ілюстрації, те, як ви можете створити "привіт світ" чисто функціональним способом, схоже на те, що ви передаєте в програмі цей стан екрану і отримуєте назад стан екрану з надрукованим на ньому "привіт світом". Тож технічно ви звертаєтесь до чистої функції, і побічних ефектів немає.
Виходячи з цього, я переглянув свою програму і: 1. По-перше, увесь стан мого додатка поставте в один глобальний об'єкт (GameState) 2. По-друге, я зробив GameState незмінним. Ви не можете його змінити. Якщо вам потрібна зміна, вам доведеться побудувати нову. Я зробив це, додавши конструктор копій, який необов'язково займає одне або кілька полів, які змінилися. 3. Кожній програмі я передаю в GameState як параметр. У межах функції, після того, як робить те, що збирається, він створює новий GameState і повертає його.
Як у мене є чисте функціональне ядро і цикл на зовнішній стороні, який подає GameState в основний цикл робочого процесу програми.
Моє запитання:
Тепер моя проблема полягає в тому, що в GameState є близько 15 різних незмінних об'єктів. Багато функцій на найнижчому рівні працюють лише на кількох об'єктах, таких як ведення балів. Отже, скажімо, у мене є функція, яка обчислює бал. Сьогодні GameState передається цій функції, яка змінює рахунок, створюючи новий GameState з новим рахунком.
Щось із цього виглядає неправильним. Функція не потребує усього GameState. Просто потрібний об'єкт Score. Тому я оновив його, щоб пройти Оцінку, і повернути лише Оцінку.
Це, здавалося, має сенс, тому я пішов далі з іншими функціями. Деякі функції вимагають, щоб я передавав 2, 3 або 4 параметри від GameState, але, оскільки я використовував шаблон у всьому зовнішньому ядрі програми, я передаю все більше і більше стану програми. Мовляв, у верхній частині циклу робочого процесу я б назвав метод, який би викликав метод, який би викликав метод тощо, аж до місця, де обчислюється оцінка. Це означає, що поточний бал передається через усі ці шари лише тому, що функція в самому низу збирається обчислити бал.
Тож тепер у мене є функції з часом десятками параметрів. Я міг би помістити ці параметри в об'єкт, щоб зменшити кількість параметрів, але тоді я хотів би, щоб цей клас був головним місцем розташування стану програми програми, а не об'єктом, який просто побудований під час виклику, просто щоб уникнути передачі у кількох параметрах, а потім розпакуйте їх.
Тож зараз мені цікаво, чи є у мене проблема в тому, що мої функції вкладені занадто глибоко. Це результат бажання мати невеликі функції, тому я рефактор, коли функція стає великим, і розділяю її на кілька менших функцій. Але це створює більш глибоку ієрархію, і все, що передається у внутрішні функції, потрібно передати на зовнішню функцію, навіть якщо зовнішня функція не працює безпосередньо на цих об'єктах.
Здавалося, просто проходження в GameState по дорозі уникло цієї проблеми. Але я повернувся до первісної проблеми передачі інформації більше функції, ніж потрібно функції.