Зверніть увагу, якщо це підходить вам, але у функціональних мовах, таких як Standard ML, все заміна за замовчуванням. Мутація підтримується через загальний ref
тип еренції. Отже int
змінна є незмінною, а ref int
змінна є контейнером, що змінюється, для int
s. В основному, змінні - це реальні змінні в математичному сенсі (невідоме, але фіксоване значення), а ref
s - "змінні" в сенсі імперативного програмування - комірка пам'яті, в яку можна записувати та читати. (Я люблю їх називати присвоюваними .)
Я думаю, що проблема з const
двома проблемами . По-перше, у C ++ бракує збору сміття, що необхідно для створення нетривіальних стійких структур даних .const
має бути глибоким, щоб мати сенс, але мати цілком незмінні значення в C ++ не доцільно.
По-друге, в C ++ вам потрібно ввімкнути, const
а не відмовитися від нього. Але коли ви const
щось забудете і пізніше це виправите, ви опинитесь у ситуації "отруєння Const", згаданої у відповіді @ RobY, де const
зміна відбуватиметься протягом усього коду. Якби const
за замовчуванням ви не застосували б const
заднім числом. Крім того, необхідність додавати const
всюди додає багато шуму коду.
Я підозрюю, що основні мови, які слідують за ними (наприклад, Java), сильно формували успіхи та спосіб мислення C та C ++. Справа в тому, що навіть при збиранні сміття більшість API колекціонування мов передбачають змінні структури даних. Той факт, що все є незмінним і незмінним, сприймається як кутовий випадок, багато в чому говорить про імперативний спосіб мислення, що стоїть за популярними мовами.
EDIT : Після роздуму над коментарем Greenoldman я зрозумів, що const
справа не стосується незмінності даних; const
кодує у тип методу, чи має він побічні ефекти на екземпляр.
Мутацію можна використовувати для досягнення референтно прозорої поведінки. Припустимо, у вас є функція, яка при виклику послідовно повертає різні значення - наприклад, функцію, з якої читається один символ stdin
. Ми могли б використовувати кеш / запам'ятати результати цієї функції для створення референтно прозорого потоку значень. Потік буде зв'язаним списком, вузли якого викличуть функцію при першому спробі отримати їх значення, але потім кешують результат. Отже, якщо stdin
містить Hello, world!
, перший раз, коли ви намагаєтеся отримати значення першого вузла, він прочитає його char
та повернеться H
. Після цього він буде продовжувати повертатися, коли ви намагаєтеся отримати його значення, цього разу повертаючи та кешуючи цей результат.H
без додаткових дзвінків для читання char
. Аналогічно, другий вузол буде читати a char
зstdin
e
Цікавим є те, що ви перетворили процес, який за своєю суттю є державним, в об'єкт, який, здавалося б, є без громадянства. Однак, щоб досягти цього, потрібно було мутувати внутрішній стан об'єкта (кешуючи результати) - мутація мала доброякісний ефект . Неможливо зробити наше, CharStream
const
навіть якщо потік поводиться як незмінна цінність. Тепер уявіть, що тут є Stream
інтерфейс з const
методами, і всі ваші функції очікують const Streams
. Ви CharStream
не можете реалізувати інтерфейс!
( EDIT 2: Мабуть, є ключове слово C ++, mutable
яке б дозволило нам обманювати і створюватиCharStream
const
. Однак ця лазівка руйнує const
гарантії - тепер ви справді не можете бути впевнені, що щось не вимкне його const
методами. Я припускаю, що це не так погано, оскільки потрібно явно вимагати лазівку, але ви все ще повністю покладаєтесь на систему честі.)
По-друге, припустимо, у вас є функції високого порядку - тобто ви можете передавати функції як аргументи іншим функціям. const
ness є частиною підпису функції, тому ви не зможете передавати нефункції const
як аргументи функціям, які очікують const
функцій. Сліпо примусове виконання const
тут призведе до втрати загальності.
Нарешті, маніпулювання const
об'єктом не гарантує, що воно не мутує якесь зовнішнє (статичне або глобальне) стан за вашою спиною, тому const
гарантії не такі сильні, як вони з'являються спочатку.
Мені не зрозуміло, що кодування наявності чи відсутності побічних ефектів у типовій системі є загальноприйнятною справою.