views:

265

answers:

6

As you know if you've read some of my other questions, I'm writing a programming language. One of my big concerns is that many languages have troubles with backwards compatibility, and I wish to avoid such issues. On one hand, I've seen a lot of pain and agony in the Python community over the switch to Python 3000 because it breaks backwards compatibility. On the other hand, I've seen C++, which started off shackled to C syntax and has never really recovered; i.e. the syntax of C is a poor fit for many C++ constructs.

My solution is to allow programmers to add a compiler directive to the file which will tell the compiler which version of the language to use when compiling. But my question is, how do other languages handle this problem? Are there any other solutions that have been tried, and how successful were those solutions?

+6  A: 

Easy : Deprecation

When new methods or functions are available, they don't simply eliminate the old ones. They just deprecated them. So, developers working on new compilers know that at some point they will need to use the new versions of those functions or in the future their program won't compile. In that way they are 'backward compatible' but at the same time enforcing the usage of the new functionality.

Freddy
Deprecation is good for library compatibility, but doesn't address language compatibility. Version 2.0 might add a foreach statement, which would be a syntax error for Version 1.0 source code.
Jim Ferrans
@Jim Ferrans: You're talking about forward compatibility, not backward compatibility (correct?) There's no reasonable way to allow a v1.0 compiler to accept v2.0 language constructs AFAIK, but the reverse is not true.
j_random_hacker
Compiler backwards compatibility means old code running on new compilers. Your example is 'forward compatibility' which means new code running on old compilers. I haven't seen those compilers yet.
Freddy
The real issue I have with depreciation is that it screws with naming. For example, your language might have a built-in "Date" class. Now let's say you realize that your Date class was fundamentally flawed and you depreciate it. The problem is that now you have to replace it with something else. And that something else probably still represents a date, but you can't call it a "Date" because that name is already taken. So you have to 1) call it something else which isn't an accurate name, or 2) design an alternative that doesn't represent a Date, (which is how we got Java's Calendar).
Imagist
@j_random_hacker: good clarification point. I was thinking of a v2.0 compiler that enforced either v1.0 or v2.0 syntax based on the "compiler directive in the file".
Jim Ferrans
@Imagist: I don't see how a class could be 'fundamentally flawed' based on its name. If something is wrong will be on its methods, variables, instances, interfaces, etc. You could create new methods, define new interfaces, fix old methods or define the whole class if you want while preserving the old methods naming and functionality, the whole class could be redesigned on a new compiler version.
Freddy
@Freddy, yes, but you have to rename it so old programs don't try to use the new class, with the different interface.
Breton
@Imagist: That's true. I think the best way is to just bite the bullet and call the new class "Date2" (a la COM interfaces). It *feels* ugly, but I've decided that such a "versioned name" actually provides the most information to someone trying to read the code. Thoughts?
j_random_hacker
+6  A: 

When something is broken, the courageous language designer must not be afraid to break backward compatibility. I know of two good ways to do it:

  • The Glasgow Haskell Compiler typically deprecates unwanted features and then drops support after two versions.

  • The Lua team have a policy that each major release (there have been 5 since 1993) may break backward compatibility, but they typically provide a compatibility layer that helps users migrate to the latest version. (Plus they are scrupulous about keeping everything available; the current version is 5.1 but I have Lua 2.5 code that I still maintain, and if I find a bug in Lua 2.5, they will fix it.)

Norman Ramsey
+1 Good advice, and good change policies.
Jim Ferrans
+2  A: 

I think you're on the right track with a compiler directive. It's may be better to package that as a command-line argument to your compiler though.

No matter what, in your compiler logic you can test against the version something like this:

if ( language_major_version > 2 )   // 2.00.00 and above
    ... normal processing ...
else
    ... emit compatibility/deprecation error ...

VoiceXML, an XML-based language for specifying voice dialogs, is one example of putting the directive in the source code:

<?xml version="1.0"?>
<vxml version="2.1">
    ...
</vxml>

Since the syntax is always well-formed XML, this is really easy to implement, almost cheating,

Jim Ferrans
As a note, I plan to implement it as a compiler directive in the source code, with the option to override the in-source version with a version passed from the command line. I am still having an issue with this, though, because I don't know exactly how to handle situations where source files have different versions.
Imagist
It can be a pain. I supported VoiceXML 1.0, 2.0, and 2.1 syntax in one "compiler" this way, but since all of these were well-formed XML it was fairly straightforward. I like @Breton's advice to map your language down into a common virtual machine or interpreter. Then at worst case you could (1) do a quick and dirty parse to determine the version, (2a) do a full parse with a v1.0-specific parser or (2b) do a full parse with a v2.0-specific parser, then (3) do the remaining compilation/interpretation phases with a standard backend.
Jim Ferrans
+2  A: 

I'm going to be the really harsh sobering voice and say: You're never going to have enough users for it to matter. Sorry, but the statistics are against you.

In the unlikely event that it does become a problem, these are the strategies I've seen used for this problem

  • Don't worry about it and just break backward compatibility

  • Keep old versions of the interpreter packaged in with the new versions and switch using some directive, or other kind of metadata

  • Make new versions a strict superset of old versions. That way, all old programs compile in the new version of the compiler/interpreter

  • Provide a converter to convert old style programs to new style programs.

  • Base the language on a virtual machine that accepts bytecode compiled from any version of the language. Ensure that there are facilities for different versions to "talk" to eachother.

  • Compromise and end up pissing everyone off instead of just half of your audience

  • New versions have a loose mode by default and a "strict" mode, the former being strictly backwards compatible, the latter removing old and busted features for those who opt in.

The good news is that none of these strategies work extremely well, so you have the opportunity to be creative and fuck up in a novel new way.

Breton
-1 for the comment about it not mattering (to which I would say it doesn't matter if it won't matter; it's still a valid question), +1 for "you have the opportunity to... fuck up in a novel new way." Actually, about +100 for that, but StackOverflow won't allow that.
Daniel Straight
A: 

Generally speaking you continue to support all the old features for at least one new version though preferably two versions into the future. Then the feature is depreciated and it is up to the user of your language to update their applications prior to the feature being dropped from your language.

Andrew Siemer
A: 

I forgot one other way that languages have dealt with backward compatibility: Stubbornly insist on never updating the language. See Donald Knuth's TEX for an example of this.

Breton
To be fair, typesetting hasn't changed much since the last version of TEX.
Imagist
True, but TEX also does not support asian languages very well, as I understand it.
Breton