TIP 505: Make [lreplace] Accept All Out-of-Range Index Values

Login
Bounty program for improvements to Tcl and certain Tcl packages.
Author:         Don Porter <dgp@users.sourceforge.net>
State:          Final
Type:           Project
Vote:           Done
Created:        26-Mar-2018
Post-History:   
Tcl-Version:	8.6.9
Vote-Results:   8/0/1 accepted
Votes-For:      DKF, KBK, JN, JD, DGP, FV, SL, AK
Votes-Against:  none
Votes-Present:  BG
Tcl-Branch:     tip-505

Abstract

Create a consistent rule to control how [lreplace] treats indices out of the range of a list's collection of elements.

Background

Using Tcl 8.5 we can examine how [lreplace] has been working as far back as we can test.

% lreplace a -5 -4 b
b a
% lreplace a -5 -1 b
b a
% lreplace a -5 0 b
b
% lreplace a -5 5 b
b
% lreplace a -1 -1 b
b a
% lreplace a -1 0 b
b
% lreplace a -1 5 b
b

So it is clear that negative index values are tolerated, and all of them are treated the same and they all anchor the replacement at the beginning of the list so that [lreplace] may be used to prepend to the list. Contrast with the treatment of index values beyond the end of the list:

% lreplace a 1 1 b
list doesn't contain element 1

So index values pointing past the end of the list are rejected, and [lreplace] cannot be used to append to a list.

Of course, no Tcl inconsistency would be complete without its own inconsistency:

% lreplace {} 0 0 b
b
% lreplace {} 1 1 b
b
% lreplace {} 20 40 b
b

So operating on an empty list apparently suspends the index value checking, and you can append once to an empty list, but no more after that.

This situation is ridiculous.

Active readers at this point might well be trying it out for themselves and are about to correct me...

% info patchlevel
8.6.8
% lreplace a 1 1 b
a b
% lreplace a 2 2 b
list doesn't contain element 2

The 8.6.* patchlevels have exhibited a variety of behaviors, courtesy of a number of buggy attempts to bytecode [lreplace]. Some releases have tolerated all index values beyond the end of the list. Recent ones have tolerated only the index value end+1 or equivalent. None of these functional changes were TIPped. They are simply the result of sensible programmers having a natural instinct to produce sensible results when they are writing code or fixing bugs. The test suite offered no constraint to keep us consistent with the weird incumbent functionality.

A fresh round of bug-fixing on the core-8-6-branch has restored consistency with 8.5 behavior, which gets us back to where out TIP history justifies us, but which is a return to the ridiculous. I don't want to return to the ridiculous, so here is the TIP to bless sensible treatment of index values beyond the end of list, consistent with how other built-in Tcl commands behave.

Proposal

Starting with Tcl 8.6.9, [lreplace] will accept all syntactically valid index values, without any regard to whether they indicate an element not in the list. All index values that point before the beginning of a list are to continue to be treated as the index value -1, pointing at the beginning of the list. This allows [lreplace] to prepend. All index values that point past the end of the list will from now on be treated as the index value end+1, pointing at the end of the list. This allows [lreplace] to append.

Compatibility

Given the mess of the 8.6.* releases, it's almost a joke to worry about compatibility, but compared with Tcl 8.5, this change is a conversion of an error case to a well-defined and useful working case. This is the kind of incompatibility we normally accept as an expansion of useful functionality.

Implementation

On the tip-505 branch.

Copyright

This document has been placed in the public domain.

History