Reassignment and Mutability
In a strict von Neumann view of computing, data is temporary. It is whatever happens to be in memory, which can be updated at any time. But some data, like constants, shouldn't change. Constants are variables that shouldn't vary. To avoid accidental changes and build safer software, we want to make constants immutable. This seems like a simple request. Yet many programming languages present a confusing view of constancy that surprises unsuspecting programmers. Consider the following equivalent JavaScript and Java programs.
There is no error in either of these programs, and they both produce the output [1, 4, 9]. But how can this be if they are modifying an array variable marked as constant? It's because const and final apply to the name, but not to the value to which the name is bound. A const or final name cannot be bound to some other value, but the value itself remains mutable.
In Java, we can prevent reassignment with final, but there is no way to make an arbitrary array or object immutable. Only the class designer can lock down an object by making its instance variables private and not defining any mutating methods. In JavaScript, Object.freeze locks down an object.
Maybe Ruby constants are true constants that lock down the name from being reassigned and the value from being mutated?
We see that a constant's value is mutable. What happens when we try to reassign a constant in Ruby?
Not even the name is locked down. We can reassign it and receive only a warning. Maybe we need some tougher languages. C has more of a backbone, doesn't it?
C does indeed lock down the value. Does it lock down the name?
Oh, dear. The name is not locked down. The meaning of const in C is exactly opposite of what const means in JavaScript.
However, the designers of C recognize that name reassignment and value mutability are independent features. They therefore allow us to add a second const modifier. The leftmost const modifier, which appears before the type, locks down the value. The rightmost const modifier, which appears before the name, locks down the name.
C is one of the few languages that allows both names and values to be marked immutable, but these markers can be overridden with a cast. To get reliable immutability, we need to use a language that hides the von Neumann architecture, like Haskell.