State and time are the same thing
State is time, time is state.
Time is state
Imagine I put an ordinary ticking quartz clock in an empty room. I walk in, and ten minutes later I walk out with two photograph prints.1 In the 1st one, the second hand is pointing at the top of the clock, in the 2nd it's pointing at the bottom. Are these two copies of the same photo, or are they two different pictures?
There's no trick here, the answer is "different photos". Since the clock looks different, time must have passed between the two. More formally, we can represent the clock as a state vector of (hours, minutes, seconds)
. Since the two pictures have different state vectors, they must represent different photos.
Now I repeat the process and come out with two prints, both showing the same time. Are these two copies of the same photo or two different photos?
It's possible for the pictures to be different but there's no way to be certain. I could have taken the two photos half a second apart so that time passed but the clock didn't tick yet. There's no observational difference between "time didn't pass" and "time passed but the state vector is the same". We can model time only passing in one second increments, as any shorter passage of time is not reflected in the state vector.
Things would be different if you had access to the clock internals. The clock is powered by a quartz crystal that oscillates at approximately 2^15 hz, and a digital circuit inside the clock is counting that number as "one second". If you could read the memory inside the clock, then you could distinguish "00:00:15" and "00:00:15 + 2^14 oscillations".
But in our current system that state is internal to the watch. Until the circuits turn the internal value into an observable value, we cannot recognize the passage of time for the clock.
The only way we can see the passage of time is by measuring changes in observable state.
State is time
Pseudocode snippet:
void f(int &c) {
*c += 2;
}
I'm trying to say that we pass a reference of a variable into f
, so int x = 1; f(x); print(x)
will output 3. Calling f(x)
permanently splits our program into two eras. Before f(x)
, all calculations with x
will get one thing. Anno Effexi, all calculations with x
will get another thing. The update to x
advanced time.
Now a more complicated case:
void f(int &c) {
*c++;
*c++;
}
Does this advance time one step or two? Depends on whether the program is single-threaded or concurrent. If it's single threaded, when f
executes there's nothing else that can read x in between the two updates, so the first mutation is "internal". Externally it looks like there was only one observable mutation x += 2
. On the other hand, if the program is concurrent, it's possible for some other thread or w/e to read x in between the two statements of f
. That makes the internal mutation observable, so time is advanced twice. There are now three eras of our program with different possible behaviors.
Changes in state matter in that they create new times.
The consequences
Some function programmers stay "shared mutable state is the enemy". I think it's more like "time is the enemy", and time represents itself as mutable state. If a state update is purely internal and cannot affect the observable state (such as a statement in an uncommitted transaction), then it does not advance time.
I find this a good model to reason about abstract systems. It also motivates formal methods concepts like bisimulation and refinement, which I really need to do an updated post on.
-
I've got a GendankenWorks instant digital camera that takes digital pictures but lets me print them too. I'm sure someone sells something like this. ↩
If you're reading this on the web, you can subscribe here. Updates are once a week. My main website is here.
My new book, Logic for Programmers, is now in early access! Get it here.