Forward compatibility

A lot of effort is often put into making things backwards compatible, i.e. making sure that a change in a new Tcl release does not break old code.

This page is about the opposite: writing extra code so new functionality can be used in older versions (of Tcl and Tk, in this context).

An example of this is glob forwards compatibility. The code on that page is about the new glob -dir path * idiom which was introduced in Tcl 8.3. A useful change, and there are so many more like it. The trouble is, once you start using it in your own scripts, you cut off the ability to use those scripts in pre-8.3 Tcl.

One way to get around this is to use conditional code, of the form "if I'm running under Tcl 8.3 or later, do <this>, else do <that>". But that may well defeat the purpose: imagine doing this to use the new "array unset" that way: it might well end up more confusing than a simple info/array condition + action.

That's where forward compatibility comes in: at the start of your app, check what version of Tcl is running, then load a bunch of extra definitions and overrides, which emulate the new features in scripts which call the available (lesser) features.

Forward compatibility is Important. Forward compatibility is Good.

There are plenty of little features which can simplify scripts just a tad more (unset without complaining, file attr changes, glob, info, not to mention Tk changes), but which I have to avoid simply because the consequence of using would have the major implication of forcing a new revision (and compile/configuration hell) onto the people using my code. That is often not justifiable: You want me to fetch Tcl/Tk N+1, figure out the build again, install it again, and introduce the potential of version conflicts or at least dependencies, just so you can use "array unset"? Yah, right..

The irony of writing lots of extra snippets of code to implement forward compatibility is that - in the end - it promotes the adoption of newer versions of Tcl/Tk and new features.

So here's a suggestion: the next time you write an "if Tcl version < N" in your code, how about writing a forward-compatibility snippet instead, and submitting it on this wiki? Just a thought... --jcw


The OOMMF project has done a lot of this. Examples below. DGP

See also Changes in Tcl/Tk for a source of things which can be done.


Here's a start:

Note that most of the above could be improved by using a utility procedure for wrapping commands.


Seems to me that these are good candidates for Tcllib!

Some like try and throw are already there. In the mean time, I EF am collecting some of these into a module of the TIL. If TIL is properly installed, accessing some of those commands from older versions of Tcl is only a matter of package require compatibility. This will work starting with 8.4 and fully on 8.5. The implementation addresses possible dependencies between the commands, e.g. whenever a forward compatibility implementation requires another command that was not present in that Tcl version. It also makes as little use of {*} for maximising 8.4 compatibility. Presence of the 8.6 feature set under the running Tcl is tested dynamically to benefit from system-wide forward compatibility solutions that would already be in place and benefit the script requiring compatibility (e.g. on older Ubuntu installations, dict is available for Tcl 8.4). At the time of writing, the set of compatible commands implemented as part of the module is: lassign, lmap, lreverse, lset, dict, throw, try, tcl::prefix, lsort, dict map. While packaging and inclusion is by EF, most of the hard work is by AMG!


Note this can be done in Tcl's C code too:


Are there other list functions which need a forward compatibility wrapper?

KMG 04-Sep-05 Added lindex to the list above for the new multiple-index feature in 8.4.


AMG: I feel a good strategy for forward compatibility is to start by implementing each new feature in terms of existing Tcl capabilities (when possible, e.g. not in the case of {*}). This forms the initial reference implementation that people can start banging on quickly to determine if the interface and functionality are really a good fit. Create an exhaustive test suite for this initial version. Then port to C and/or integrate with the core. Verify the new code using the test suite, and expand the test suite to cover new corner cases as they crop up. Continue to maintain both the compatible and integrated versions so people stuck with old Tcl can fall back on the compatible version yet be reasonably assured their program will continue to work, albeit with reduced performance.

One difficulty is maintaining and publishing the compatible code. I don't think this is suited to the mission of Tcllib, though a similar package specifically geared to compatibility could be created. EF mentions one above. Maybe that could be expanded. However, this compatibility library should probably be owned by the TCT since in addition to compatibility it'll also collect the initial version of experimental features before they ever get integrated with the core. Yet, this can't be part of the core Tcl repository because the whole point is being able to mix and match versions. It would work as a nested Fossil checkout in pkgs though.

A more severe difficulty is {*}. Should compatibility code be written to not use it? Should compatibility code be written both ways? Should Tcl 8.4 be considered obsolete and Tcl 8.5 be the target platform? I've previously targeted 8.4 for my professional compatibility efforts, but I've been breathing much easier since allowing myself to target 8.5 instead.

Targeting 8.5 isn't a panacea since I'm still without [coroutine] and [chan pipe] and half-close and other important core capabilities. This is an even bigger problem than {*} where the code can at least be rewritten to do without, yet how do I emulate [coroutine]? To me, the value of [coroutine] is the ability to [yield] through many layers of the call stack, most notably user code that I can't rewrite. [chan pipe] could perhaps be emulated with named pipes, but how would that work on Windows? And half-close emulation might require leveraging a non-Tcl intermediate process between Tcl and the process (or pipeline) actually doing the useful work, which would also be tricky on Windows. So no, this forward compatibility strategy is not always possible.