Прозорість посилань (інформатика)
Прозорість посилань і непрозорість посилань — це властивості частин комп'ютерних програм. Кажуть, що вираз прозорий за посиланнями, якщо його можна замінити на його значення і це не вплине на поведінку програми (тобто ми отримаємо нову програму, що на ті ж вхідні дані видає такі ж вихідні). Протилежним терміном є непрозорість посилань.
В математиці всі функції прозорі за посиланнями, в програмуванні це не завжди так. Важливість прозорості посилань полягає в тому, що вони дозволяють програмісту та компілятору робити здогади про поведінку програми. Це може допомогти в доведенні правильності, спрощенні алгоритму, допомагає при зміні коду без отримання нових помилок або оптимізації коду в сенсі мемоізації, усунення спільних підвиразів або паралелізації.
Прозорість посилань є одним з принципів функційного програмування. Тільки прозорі за посиланнями функції можуть бути мемоізовані (перетворені в тотожні функції, які використовують кешовані результати). Деякі мови програмування[які?] забезпечують засоби для гарантування прозорості посилань. Деякі ж вимагають дотримання прозорості посилань у всіх функціях.
Через те, що прозорість посилань вимагає однакового результату на однаковому вході в будь-який час, прозорий за посиланнями вираз є детермінованим за визначенням.
Приклади й контрприклади
Якщо в виразі задіяні лише чисті функції, тоді вираз буде прозорим за посиланнями. Також, деякі нечисті функції можуть бути використані у виразі, якщо їх значення не впливають на значення виразу та їх побічні ефекти незначимі.
Візьмемо функцію, що не приймає параметрів і повертає дані, введені з клавіатури. Припустимо, її виклик такий: GetInput()
. Значення повернуте функцією GetInput()
залежить від вводу користувача, отже різні виклики GetInput()
з однаковими параметрами (порожній список) можуть повертати різні значення. Звідси, GetInput()
ані детермінована, ані прозора за посиланнями.
Витонченіший приклад — функція, що використовує глобальну змінну (змінну з динамічною областю видимості або лексичне замикання), щоб полегшити собі обчислення результату. Через те, що ця змінна не передається як параметр, але може бути змінена, результати послідовних викликів можуть відрізнятись, навіть якщо параметри тотожні. В чистому функційному програмуванні використовується підхід єдиного присвоєння, тобто змінні після присвоєння їм значень стають незмінними об'єктами.
Арифметичні операції прозорі за посиланнями: 5*5
можна замінити на 25
. В дійсності, всі функції прозорі за посиланнями в математичному сенсі: sin(x)
прозора, бо завжди повертає однакове значення для певного x
.
Присвоєння не прозоре. Наприклад, вираз на C x = x + 1
змінює значення надане x
. Припустимо x
початково має значення 10
, два послідовні обчислення виразу дають, відповідно, 11
і 12
. Звідси видно, що заміна x = x + 1
на 11
або 12
надасть програмі іншого сенсу, отже вираз не прозорий за посиланнями. Однак, виклик функції int plusone(int x) {return x+1;}
є прозорим, бо вона явно не змінює x, отже не має побічних ефектів.
В більшості мов[яких?] вираз print("Вітаю, світе!")
не прозорий, бо його заміна на значення, що повертається, змінює поведінку програми, бо «Вітаю, світе!» не надрукується.
today()
теж не прозора, бо видає різні значення кожного нового дня.
Відмінність від імперативного програмування
Якщо заміна виразу на його значення дійсна тільки в певній точці виконання програми, тоді цей вираз не прозорий за посиланнями. Визначення і впорядкування цих точок перебігу (англ. sequence point) це теоретичні підвалини імперативного програмування і частина семантики імперативних мов програмування.
Однак через можливість обчислення виразів прозорих за посиланнями будь-коли, необов'язково визначати точки перебігу й взагалі будь-які гарантії послідовності виконання. Програмування виконане на такий лад називається чисто функційним програмуванням.
Однією з переваг написання коду з прозорими посиланнями є те, що розумному компілятору легше виконувати статичний аналіз коду і використовувати кращі оптимізації. Наприклад, у випадку С присутнє погіршення швидкодії через додавання до тіла циклу дорогої, в сенсі виконання, функції, навіть якщо виклик функції можна винести за тіло циклу без зміни результату програми. Програміст змушений робити ручні правки, які можливо позначаться на зручності читання коду. А якщо компілятор в змозі визначити, що виклик функції прозорий за посиланнями, він може здійснити це перетворення автоматично.
Основним недоліком мов, що наполягають на прозорості посилань є те, що в них послідовність дій, яка природно може бути записана як покрокова послідовність в імперативному стилі, набуває незграбного й розлогого вигляду. Такі мови часто містять механізми, що дозволяють виконати такі задачі простіше, зберігши при цьому чистоту функційності мови, наприклад, DC-граматики й монади.
У випадку прозорості посилань немає різниці між посиланням на об'єкт і самим об'єктом. Інакше така різниця може бути легко введена й використана.