A better way of managing backwards compatibility?

Prudence via Digitalmars-d digitalmars-d at puremagic.com
Thu Sep 3 10:48:21 PDT 2015


Dealing with changes to standardized interfacing such as api 
function names, namespaces, file names/module names, etc is 
usually impossible to do because it severs the code written 
before the change from the new compiler. Most people think this 
is the way to do it and there is no, better, alternative.

Well, *NOW* there is:

One could completely change D(could replace it with a Fortran 
version, if desired) yet still keep backwards compatibility!!!! 
And it would be relatively easy.

"How can this be possible", you exclaim with relative disdain!!

Well, there are two distinct but compatible ways:


1. Essentially keep track of the version the compiler that is 
currently being used to compile the project, somewhere. (or one 
might be able to infer this from the dates, as long as one has a 
date to version mapping, but this is not accurate)

As long as those binaries of the compiler(and source would be 
nice) are somewhere, the user can be informed to download the 
correct version and/or have the D compiler automatically do this 
for it.

Note though, the requirement for D is only that it can do the 
checking and possibly downloading of another compiler. In fact, 
it could have it such delegating facilities to compile Fortran by 
similar means or it could be the D3 compiler that downloads D1 
for some ancient source code.


2. Write a translation process that essentially "updates" the 
source code to work.

Suppose D **wanted** to change a keyword for some reason or 
another. The compiler first runs the code through the translation 
process which mostly is just a token replacer but it could be 
more advanced and modify function differences(such as a swapping 
of two parameters).

The good news, is that such a feature is easy to implement as it 
is just a mapping of the token stream to another. The hard part 
is getting everyone on a high enough level to put such changes in 
the translation process when they modify the compiler or library 
or whatever.

For example, if we wanted to change "for" with "pour" then the 
parser just as an additional step. Instead of something like: 
parse(token[i]); we would have: parse(translate(token[i])); Where 
translate is just a string to string map, and we could make 
translate more complex by dealing with 
context(parse(translate(token, i)).

Again, one would have to know the version in some way.

We can think of 1 and 2 as different ends of the granular 
spectrum. 1 will get the correct version used and use it. It has 
to work, if not, then it wouldn't have worked anyways(user/setup 
issue) since this is sort of just automating what a user can do. 
I believe there are already tools that sort of emulate 
this(allows you to switch between versions easily but they are 
dumb(no memory and no automation)). 2 generally deals with simple 
changes.


The good news is that all this can come from just knowing the 
version to compile the source with. This then gives the compiler 
designer the freedom not to worry about naming stuff perfectly. 
Documentation is not a problem, as the same translation and 
versioning can be automated in the same way using the same data.

e.g., go to the docs, if you are using an old version, select the 
version, your correct documentation shows up.


What these two processes, together, do, is essentially gives a 
discrete "history" of the compiler. It would be like an 
continuous incremental backup of every change to a compiler, but 
since most changes do not effect source code backwards 
compatibility, One doesn't need every single change.

It also allows one to migrate to new versions seamlessly.

Imagine going from D1 to D2, but it's just D anyways because the 
versions "don't matter" anymore.

1. The compiler will try to translate the D1 source to the D2 
source through a series of micro translations(for each version we 
would have a translate). If it fails at some point, it will 
report to the user the "errors in translation" which can be due 
to a syntax change that is known to break versioning. (e.g., 
change for(i=0;i<10;i++) to for i=0,9,1, which breaks code 
because of semantics... although with a more intelligent 
translator this specific case can easily be handled)

2. If step one fails, which depends on how many times it has to 
be called, the further way the versions we are going between the 
more likely step 1 will fail. In that case we revert the "exact" 
compiler needed by finding and using the correct compiler that 
the source code was designed with.


Of course! We would want this to be build in to the compiler from 
day one. But step 2 saves us with D!! Since almost every version 
has been saved, we can pretty much use step 2 the whole time.

What to this do for everyone when implemented properly? Simply 
keep the versions with the source code somehow(embed in source in 
a comment at the bottom of a file or in a configuration file 
somewhere), and keep the translation changes up to don't on 
compiler modification, and include this feature in the compiler, 
and then ***NO ONE*** will have to worry about versions again. 
(at worse, if they want to use the tools manually to translate 
the software to a working version then manually edit it to get it 
past a break(essentially replace step 1 with actually fingers on 
the keyboard... and if their smart, they could add those changes 
to the translation database for that version which the compiler 
could use(pull from the cloud))










More information about the Digitalmars-d mailing list