TIP 215: Make [incr] Auto-Initialize Undefined Variables

Login
Bounty program for improvements to Tcl and certain Tcl packages.
Tcl 2017 Conference, Houston/TX, US, Oct 16-20
Send your abstracts to tclconference@googlegroups.com
by Aug 21.
Author:         Andreas Leitgeb <avl@logic.at>
Author:         Don Porter <dgp@users.sf.net>
State:          Final
Type:           Project
Vote:           Done
Created:        25-Aug-2004
Post-History:   
Keywords:       Tcl
Tcl-Version:    8.5

Abstract

Unlike append and lappend, incr currently does not auto-create yet-undefined variables. This TIP proposes to make incr's behaviour in this regard more like the aforementioned commands.

Rationale

A quite common task is counting the number of occurrences of items in a given list. The usual solution is to iterate the list, and for each item, increment the associated value in a tcl-array. As of now this requires a separate step of determining the not-yet-existence and eventual initialization to 0 or alternatively catch'ing errors from incr and setting the variable, if an error was raised.

If we instead alter incr to treat non-existant variables as if they contained the value 0, this would be more like the auto-initializing behaviour of append and lappend, and would make writing code that does this sort of summing up much easier. It is also very similar to the way that the dict incr subcommand operates.

No Change for Variables that Contain Non-Integers

Just as lappend does complain if passed a variable whose value is an invalid list (e.g. a single open-brace), so it appears reasonable for incr to still throw an error if the variable contains something that is not a number.

The empty string is invalid as an operand for expr's integer operators, so it should remain illegal to incr an existing variable that contains an empty string.

Further Special Cases

If a variable passed to incr is not yet existing, but linked to some other not-yet existing var, or if it is traced, then of course it would add flesh to that existing husk. Care should be taken that any write traces only trigger once (like for lappend), not twice (as in: for initializing and then for incrementing).

Proposal

The current incr command behaves like the following proc:

 proc incr {varName {increment 1}} {
     upvar 1 $varName v
     return [set v [expr {$v + $increment}]] ;# read/write trace
 }  

It is proposed to make incr behave like the following modified proc:

 proc incr {varName {increment 1}} {
     upvar 1 $varName v
     set code [catch {set v} value] ;# read trace
     if {$code} {
         return [set v $increment]  ;# write trace
     }
     return [set v [expr {$value + $increment}]] ;# write trace
 }  

Please note these example procs are meant to illustrate the proposed change only. They do not fully reflect the exact function of incr (limiting to only integer values, for example).

For a more concrete illustration of the proposal, see the reference implementation, SF patch #1413115 http://sourceforge.net/tracker/index.php?func=detail&aid=1413115&group_id=10894&atid=310894 .

Discussion

There have been comments that incr modified as proposed would lose one means of typo-detection: If the varname is mistyped, then instead of throwing an error, it would turn into a noop.

My (TIP-originator) answer to this: relying on thrown exceptions for mistyped varnames is a very weak strategy in dynamic (non-compiled) languages like tcl, anyway, because it would require a 100%-test-coverage of execution-paths.

Nevertheless, this shouldn't exclude discussion about less typo-forgiving alternatives:

Proposed (by others) alternative #1: make incr accept a second optional argument init which, if specified, will cause incr to accept non-existent variables and initialize them with the value init. One disadvantage of this behaviour is, that it is at odds with append and lappend behaviour, which would rather imply that if more than one argument is given after the varname, then each of them would be added in row. Another con would be, that it would not be intuitive, whether the resulting value would be init or (init + increment). Similar alternatives mentioning new option -initValue are over-verbose imho.

Another way to partially reduce this proposal's forgiveness would be to allow autoinitialization only for array-elements. This looks grossly unorthogonal at first sight, but addresses the fact, that the primary reason for TIP 215 was counting occurrences of strings, which are then made keys in an array. The danger of mistyped array-keys is surely much lower than that of mistyped variable names. See [224]

Copyright

This document has been placed in the public domain.

History