←back to thread

498 points azhenley | 6 comments | | HN request time: 1.103s | source | bottom
Show context
EastLondonCoder ◴[] No.45770007[source]
After a 2 year Clojure stint I find it very hard to explain the clarity that comes with immutability for programmers used to trigger effects with a mutation.

I think it may be one of those things you have to see in order to understand.

replies(17): >>45770035 #>>45770426 #>>45770485 #>>45770884 #>>45770924 #>>45771438 #>>45771558 #>>45771722 #>>45772048 #>>45772446 #>>45773479 #>>45775905 #>>45777189 #>>45779458 #>>45780612 #>>45780778 #>>45781186 #
rendaw ◴[] No.45770924[source]
I think the explanation is: When you mutate variables it implicitly creates an ordering dependency - later uses of the variable rely on previous mutations. However, this is an implicit dependency that isn't modeled by the language so reordering won't cause any errors.

With a very basic concrete example:

x = 7

x = x + 3

x = x / 2

Vs

x = 7

x1 = x + 3

x2 = x1 / 2

Reordering the first will have no error, but you'll get the wrong result. The second will produce an error if you try to reorder the statements.

Another way to look at it is that in the first example, the 3rd calculation doesn't have "x" as a dependency but rather "x in the state where addition has already been completed" (i.e. it's 3 different x's that all share the same name). Doing single assignment is just making this explicit.

replies(10): >>45770972 #>>45771110 #>>45771163 #>>45771234 #>>45771937 #>>45772126 #>>45773250 #>>45776504 #>>45777296 #>>45778328 #
1. jstimpfle ◴[] No.45771163[source]
The immutable approach doesn't conflate the concepts of place, time, and abstract identity, like in-place mutation does.

In mutating models, typically abstract (mathematical / conceptual) objects are modeled as memory locations. Which means that object identity implies pointer identity. But that's a problem when different versions of the same object need to be maintained.

It's much easier when we represent object identity by something other than pointer identity, such as (string) names or 32-bit integer keys. Such representation allows us to materialize us different versions (or even the same version) of an object in multiple places, at the same time. This allows us to concurrently read or write different versions of the same abstract object. It's also an enabler for serialization/deserialization. Not requiring an object to be materialized in one particular place allows saving objects to disk or sending them around.

replies(1): >>45774510 #
2. SpaceNoodled ◴[] No.45774510[source]
The hardware that these programs are running on store objects in linear memory, so it doesn't not make sense to treat it as such.
replies(2): >>45774729 #>>45780295 #
3. wtallis ◴[] No.45774729[source]
DRAM is linear memory. Caches, less so. Register files really aren't. CPUs spend rather a lot of transistors and power to reconcile the reality of how they manipulate data within the core against the external model of RAM in a flat linear address space.
4. jstimpfle ◴[] No.45780295[source]
Can you clarify?
replies(1): >>45781441 #
5. repstosb ◴[] No.45781441{3}[source]
Modern CPUs do out-of-order execution, which means they need to identify and resolve register sharing dependencies between instructions. This turns the notional linear model of random-access registers into a DAG in practice, where different instructions that might be in flight at once actually read from or write to different "versions" of a named register. Additionally, pretty much every modern CPU uses a register renaming scheme, where the register file at microarchitecture level is larger than that described in the software-level architecture reference, i.e. one instruction's "r7" has no relationship at all to another's r7".

Caches aren't quite as mix-and-match, but they can still internally manage different temporal versions of a cache line, as well as (hopefully) mask the fact that a write to DRAM from one core isn't an atomic operation instantly visible to all other cores.

Practice is always more complicated than theory.

replies(1): >>45785832 #
6. FooBarBizBazz ◴[] No.45785832{4}[source]
Realistically, the compiler is building a DAG called SSA; and then the CPU builds a DAG to do out of order execution, so at a fine grain -- the basic block -- it seems to me that the immutable way of thinking about things is actually closer to the hardware.