Ask HN: What programming languages distinguish between “old” and “new” values?

1 points by aappleby ↗ HN
Suppose I have this trivial C++ program:

  #include <stdio.h>

  int a = 0;
  int b = 0;

  void update_state() {
    a = b + 1;
    b = a + 1;
  }

  int main() {
    for (int i = 0; i < 3; i++) {
      update_state();
      printf("%d %d\n", a, b);  
    }
    return 0;
  }
This will print

  1 2
  3 4
  5 6
which shouldn't be too surprising. What it does _not_ print is:

  1 1
  2 2
  3 3
because "b = a + 1" is reading the "new" value of A, not the "old" value as you might expect if you were a new programmer just skimming through the source. If we wanted that behavior we could write

  void update() {
    int a_old = a;
    int b_old = b;
    a = b_old + 1;
    b = a_old + 1;
  }
or

  void update() {
    int a_new = b + 1;
    int b_new = a + 1;
    a = a_new;
    b = b_new;
  }
but in both cases we have to introduce new temporary variables to hold a copy of either the "old" or "new" state so we don't clobber one of them. If we could annotate our variable names with "oldness" and "newness" we could write something like this:

  void update() {
    a_new = b_old + 1;
    b_new = a_old + 1;
  }
But that won't work in plain C because "_old" and "_new" aren't meaningful suffixes. More importantly, we can't realistically implement any way of explicitly distinguishing between old and new values in C++ at all - classes and templates don't quite give us enough flexibility to do so.

In this trivial example it's easy to see why the different update() methods produce different results, but in large systems it's surprisingly easy to introduce subtle bugs by changing the order of function calls : ClassA expects to read some "new" state from ClassB, code gets refactored, ClassA now inadvertently reads "old" state from ClassB, everything appears to work fine until one day a timing issue causes ClassA and ClassB to get out of sync and everything blows up.

After looking around at existing programming languages (and some CS papers from Google Scholar), it doesn't appear that any major programming languages support this sort of annotation. The closest you can get is the "<=" operator in Verilog, which means something like "assign the RHS to the LHS, but don't do it yet - do it after the current simulation step is done running".

I feel like this is probably a thoroughly covered topic already, but my research skills are failing to turn up anything relevant. Is there some computer sciencey term I'm unaware of to describe languages that allow for these sorts of old/new distinctions?

8 comments

[ 3.2 ms ] story [ 28.3 ms ] thread
Python:

    (a, b) = (b + 1, a + 1)
The two expressions that add 1 are evaluated first, a tuple containing the anonymous results is created, which is unpacked and assigned to the names on the left side. This sidesteps the problem of old/new values.
That works for the trivial example, but in a larger program I can't realistically say (a,b,c,d,e,f......zzz) = (....) to get the equivalent of an "atomic" update to the program's state.
Then don't do that. Python also has mechanisms for handling sequences of values, passing them to functions, etc. But that's getting off the point of your question.

You asked "what programming languages ...?". I gave an example of how the python language gets around the problem scenario you used as an example. I'm not suggesting you use python or any other language, just providing one answer to your question.

This is why it's usually a good idea to minimize mutable state, especially global mutable state. Pure functional languages like Haskell go even further and prohibit mutable state entirely.

One way to rewrite your example to do what you want would be with the following changes:

int g_a = 0;

int g_b = 0;

void update_state( int a, int b ) {

    g_a = b + 1;

    g_b = a + 1;
}

Then call update_state like this:

update_state( g_a, g_b );

This works because in C function parameters are passed by value by default.

As for languages that distinguish "old" from "new" values, I don't know of any and I'm not sure there's a well-defined way to do it apart from function calls.

It's a slight technicality, but Haskell does allow mutable state, it just has to be wrapped in a monad (e.g. IO or MutVar#).
SQL kind of supports this through the use of transactions. That approach has been generalized into "transactional memory."

https://en.wikipedia.org/wiki/Transactional_memory

True, that's similar - but I wouldn't say that SQL alone is enough to build an application.
Not for sane people. Financial institutions may disagree though. :P I’ve heard of people who all day long just write PL/SQL procedures.