3 ms·
The killer IMO is the combination of mutability and implicit references. With either immutability or explicit references, the hazards are greatly lessened. In t
by mmaniac 2y ago
The killer IMO is the combination of mutability and implicit references. With either immutability or explicit references, the hazards are greatly lessened. In the absence of mutability, there's no observable difference between passing a reference and passing a value. With explicit references, there's always an indication when objects are liable to nonlocally mutate.
Go and C are quite wise to use pass by value and opt-in references. It avoids the programmer's burden of immutability by default while making it more obvious* when to be aware of potential mutation. A programming style incorporating local mutation also comes very naturally, which is simple, practical, and remains easy to reason about.
*Go and Rust would be wiser if they retained -> from C so that this obviousness would remain without having to check type declarations which may even have been inferred.
Python gets hit the worst by this combination. On top of having mutable references everywhere, it also doesn't make a big deal about object identity (unlike e.g. Java) and has a lot of declarative constructs. Which means you get surprising stuff like this.
>>> foo = [[]] * 10
>>> foo[0].append("bar")
>>> foo
[['bar'], ['bar'], ['bar'], ['bar'], ['bar'], ['bar'], ['bar'], ['bar'], ['bar'], ['bar']]
>>> def baz(arg = []):
... print(arg)
... arg.append("qux")
...
>>> baz()
[]
>>> baz()
['qux']