Які переваги пропорційної прозорості для програміста?


18

У програмі, які переваги мають референтна прозорість ?

RT є однією з головних відмінностей між функціональними та імперативними парадигмами, і часто використовується прихильниками функціональної парадигми як явна перевага перед імперативною; але всіма своїми зусиллями ці захисники ніколи не пояснюють, чому це користь для мене як програміста .

Звичайно, вони отримають свої академічні пояснення того, наскільки "чистим" та "елегантним" він є, але як це зробити краще, ніж менш "чистий" код? Як мені це корисно в щоденному програмуванні?

Примітка. Це не дублікат Що таке референтна прозорість? Остання стосується теми, що таке RT, тоді як це питання стосується її переваг (що може бути не таким інтуїтивним).




3
Довідкова прозорість дозволяє використовувати вирівнювальні міркування для: 1) Доведення властивостей коду та 2) запису програм. Є кілька книг про Haskell, де автори повинні, як ви можете почати з деяких рівнянь, які ви хочете, щоб функція була повністю виконана, і, використовуючи лише екваціональне міркування, ви в кінцевому підсумку отримуєте реалізацію зазначеної функції, що, отже, безумовно, правильно. Тепер, наскільки це можна застосувати у програмі "щоденне", ймовірно, залежить від контексту ...
Bakuriu

2
@err Вам подобається код, який простіше зробити рефактор, оскільки ви знаєте, чи викликати функцію двічі - це те саме, що зберігати її значення у змінній, а потім використовувати вказану змінну двічі? Ви б сказали, що це користь для вашого щоденного програмування?
Андрес Ф.

Перевага полягає в тому, що вам не потрібно витрачати час на роздуми про референтну непрозорість. Як ніби як переваги змінних полягають у тому, що вам не потрібно витрачати час на роздуми про розподіл реєстру.
користувач253751

Відповіді:


37

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

Візьмемо приклад computeProductPriceметоду.

Чистий метод вимагає від вас кількості товару, валюти і т. Д. Ви знаєте, що коли метод буде викликаний з однаковими аргументами, він завжди дасть однаковий результат.

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

Нечистий метод буде складнішим у використанні та налагодженні. Оскільки це залежить від стану змінних, крім аргументів, і можливо їх зміни, це означає, що він може давати різні результати при виклику декількох разів, або не мати однакову поведінку, коли не викликається взагалі або викликається занадто рано або занадто пізно.

Приклад

Уявіть, що в рамках є метод, який аналізує число:

decimal math.parse(string t)

Він не має референтної прозорості, оскільки це залежить від:

  • Змінна середовища, яка вказує систему нумерації, тобто База 10 або щось інше.

  • Змінна в mathбібліотеці, яка визначає точність розбору чисел. Тож зі значенням 1, розбір рядка "12.3456"дасть 12.3.

  • Культура, яка визначає очікуване форматування. Наприклад, з fr-FR, синтаксичний розбір "12.345"дасть 12345, оскільки символом розділення повинен бути ,, а не.

Уявіть, як легко та важко було б працювати з таким методом. З тим самим входом ви можете мати докорінно різні результати залежно від моменту, коли ви викликаєте метод, оскільки щось десь змінило змінну середовища або переключило культуру або встановило іншу точність. Недетермінований характер методу призведе до більшої кількості помилок і більшого налагодження кошмару. Телефонувати math.parse("12345")та отримувати 5349як відповідь, оскільки якийсь паралельний код розбирав восьмеричні числа, не приємно.

Як виправити цей очевидно зламаний метод? Запроваджуючи референтну прозорість. Іншими словами, позбувшись глобального стану та перемістивши все до параметрів методу:

decimal math.parse(string t, base=10, precision=20, culture=cultures.en_us)

Тепер, коли метод чистий, ви знаєте, що незалежно від того, коли ви будете викликати метод, він завжди дасть однаковий результат для тих же аргументів.


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

3
Зауважте, що є обмеження щодо того, наскільки ви можете бути прозорими. Зроблення packet = socket.recv()референтно прозорого досить перемагає точку функції.
Марк

1
Повинна бути культура = culture.invariant. Якщо ви не хочете випадково створити програмне забезпечення, яке працює належним чином лише в США.
користувач253751

@immibis: хм, гарне запитання. Якими були б правила розбору invariant? Або вони такі ж, як і en_usв такому випадку, навіщо турбуватися, або вони відповідають якійсь іншій країні, у такому випадку, якій і чому ця замість цього en_us, або у них є свої конкретні правила, які ні в якому разі не відповідають жодній країні , що було б марно. Не існує справжньої відповіді між 12,345.67та 12 345,67: будь-які «правила за замовчуванням» працюватимуть у кількох країнах і не працюватимуть для інших.
Арсеній Муренко

3
@ArseniMourzenko Це, як правило, "найнижчий загальний знаменник" і подібний до синтаксису, який використовують багато мов програмування (що також є інваріантною культурою). 12345розбирає , як 12345, 12 345або 12,345чи 12.345помилка. 12.345розбирається як інваріантне число з плаваючою комою завжди дає 12.345, згідно з умовами використання мови програмування. як десятковий роздільник. Рядки сортуються за кодовими кодами Unicode та залежно від регістру. І так далі.
користувач253751

11

Чи часто ви додаєте крапку до точки в коді та запускаєте додаток у відладчику, щоб розібратися, що відбувається? Якщо це зробити, це багато в чому тому, що ви не використовуєте еталонну прозорість (RT) у своїх проектах. І тому доведеться запустити код, щоб розібратися, що він робить.

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

Чим простіший код - це читати та міркувати, тим простіше підтримувати та виявляти помилки, тому це економить час та гроші для вас та вашого роботодавця.


10
"Після того, як ви почнете додавати змінні змінні, деякі з яких мають поза однією функцією, ви не можете просто прочитати код, вам доведеться його виконати, або в голові, або в налагоджувачі, щоб дізнатися, як він справді працює. ": Гарна думка. Іншими словами, референтна прозорість означає не лише те, що фрагмент коду завжди буде давати однаковий результат для одних і тих же входів, але і те, що отриманий результат є єдиним ефектом цього фрагмента коду, що немає жодної іншої прихованої сторони ефект, як зміна змінної, яка була визначена далеко в іншому модулі.
Джорджіо

Це хороший момент. У мене є проблема з тим, що простіший аргумент коду є для читання / аргументації, оскільки простіше читати чи міркувати - дещо розпливчастий та суб'єктивний атрибут коду.
Ейал Рот

Після того, як ви почнете додавати змінні змінні, деякі з яких мають сферу дії, що перевищує одну функцію, але чому операція присвоєння не перешкоджає, навіть коли область змінної області локальна для функціонування?
rahulaga_dev

9

Люди кидають навколо поняття "легше міркувати", але ніколи не пояснюють, що це означає. Розглянемо наступний приклад:

result1 = foo("bar", 12)
// 100 lines of code
result2 = foo("bar", 12)

Є result1і result2той же або різні? Без еталонної прозорості ви поняття не маєте. Ви повинні насправді прочитати тіло, fooщоб переконатися, а можливо, і тіло будь-яких функцій, що fooвикликає, тощо.

Люди не помічають цього тягаря, тому що вони звикли до нього, але якщо ти будеш працювати в чисто функціональному середовищі місяць-два, то повернись, ти відчуєш це, і це величезна справа .

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


1
Ви згадали , що посилальна прозорість дозволяє міркувати про результат звернень до Foo () і чи знає result1і result2те ж. Ще один важливий аспект полягає в тому, що якщо він foo("bar", 12)є референсно прозорим, то вам не доведеться запитувати себе, чи викликав цей виклик десь інший ефект (встановити деякі змінні? Видалений файл? Що завгодно).
Джорджіо

Єдина «референтна цілісність», з якою я знайома, стосується реляційних баз даних.
Марк

1
@Mark Це помилка. Карл мав на увазі референтну прозорість, як це очевидно з решти його відповіді.
Андрес Ф.

6

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

У вас є функція, і ви можете краще міркувати, що вона робить, оскільки немає зовнішніх факторів, які потрібно враховувати, для даного вводу вихід завжди буде однаковим. Навіть в моїй імперативній мові я намагаюся максимально дотримуватися цієї парадигми, наступне, що в основному автоматично випливає з цього, це: невеликі легкі для розуміння функції замість жахливих лінійних функцій 1000+, в яких я іноді запускаюсь.

Ці великі функції роблять магію, і я боюся торкнутися їх, тому що вони можуть зламати ефектними способами.

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.