Каквото Александър каза. Малко ще се разсея в размисли за дизайн:
Няма нищо сложно в това да се имплементира някакво mutability, както въпроса на Петър показва. Безумно лесно е да се направи:
num = Lazy(10)
denom = Lazy(20)
ratio = num / denom
answer = ratio.force()
denom.value = 0
newAnswer = num / denom
print(answer)
print(newAnswer)
Където .value =
е някакъв начин да мутираме такова число. Обаче това поражда неудобни въпроси. Например:
Трябва ли answer
да е равен на newAnswer
?
И в двата отговора има логика. Да, защото са създадени от едни и същи обекти. Не, защото обектите са имали различна стойност при различните създавания.
Къде ще се породи грешката?
Да допуснем, че отговора на предния въпрос е "да". Тогава? При принтиране на newAnswer
? При принтиране на answer
? При denom.value = 0
? Всичките имат логика. Последното е изключително дървено за имплементация.
...и ред други.
Има и по-тънък момент. Като дизайнер на кода, вие отговаряте на тези въпроси веднъж. Може би след задълбочен размисъл и разглеждане на алтернативи. Потребителите на кода, обаче, трябва да отговарят на тези въпроси всеки път, когато го ползват. На човек му е трудно да държи в главата си всички подробности на всички инструменти. Затова предпочитам по-простата семантика, която не поражда въпроси и е напълно очевидна (immutable), пред по-сложната, в която въпросите имат ред адекватни отговори и вярният не е очевиден. Това може да се разглежда като principle of least surprise.
Добавете в картинката, че две седмици след като сте написали (и забравили) един код се превръщате от автор в потребител, и кръгът се затваря. Почвате да се чудите какво точно решихте и защо точно така.
Immutability-то е голяма стъпка в посоката на по-прост и разбираем код (за сметка на производителност). Точно както Python, Java и дори C.