Author: Frédéric Bonnet <[email protected]>
State: Final
Type: Project
Vote: Done
Created: 13-May-2018
Post-History:
Keywords: Tcl,array
Tcl-Version: 8.7
Vote-Results: 9/0/0 accepted
Votes-For: DKF, BG, KBK, JN, JD, DGP, FV, SL, AK
Votes-Against: none
Votes-Present: none
Tcl-Branch: tip-508
Abstract
This TIP proposes a new array default subcommand that allows default values for arrays, causing that value to be returned any time an attempt is made to access an element of the array that isn't present.
Rationale
For its most basic usages, a Tcl array variable can be seen as a collection of scalar variables. However, an array also has its own existence as a variable and provides specific actions on itself and on its elements. For example, the regular way to check for a variable existence is to use the info exists command, and this is true for both arrays and scalars (including array elements); likewise, the set command is used to read and write the value of a scalar vars and array elements, however array variables have no value in themselves but provide a way to get and set several of its elements at once.
For more advanced usages, one must get past the simple ‘collection of scalars’ viewpoint. A typical example is sparse arrays: at present, an implementation requires a combination of array exists (or info exists) and set: the former to test the presence of a key in an array, the latter to get or set the actual value, and in this order. Failure to do so is likely to cause runtime errors.
This document therefore proposes to add a new array default subcommand in order to support such advanced usages. Sparse arrays built on this new feature will be simpler, more robust, faster, all the while being memory efficient.
Specification
A new array default subcommand will be created with the following syntax:
array default option arrayName ?arg?
The following option values are supported:
- array default get arrayName — Get the default value for the array arrayName. Raise an error if arrayName is not an array variable or if it has no default value.
- array default set arrayName value — Set the default value for the array arrayName. If the variable arrayName does not already exist, it is created with an empty array value. Raise an error if arrayName is an existing scalar variable or an invalid array name.
- array default exists arrayName — Tests whether the array arrayName has a default value. Return false if arrayName does not exist. Raises an error if a variable arrayName exists but is not an array variable.
- array default unset arrayName — Unsets arrayName's default value so that the array no longer has a default. Does nothing if arrayName does not exist. Raises an error if arrayName is not an array variable.
The default value is only used as a last resort where an error would normally occur when accessing a non-existing array element. Read traces are processed before that point and are free to set the element value or modify the default value.
Examples
Initial state:
% array exists var
0
% array default exists var
0
% array default get var
"var" isn't an array
% set var(a)
can't read "var(a)": no such variable
Create array with one entry:
% set var(a) 1
1
% array exists var
1
% array default exists var
0
Attempt to read missing entry:
% set var(b)
can't read "var(b)": no such element in array
Add default value:
% array default set var 0
% array default exists var
1
% array default get var
0
Default value is only used by missing entries:
% info exists var(a)
1
% set var(a)
1
% info exists var(b)
0
% set var(b)
0
You can lappend
to a missing entry and it will behave as expected:
% lappend var(b) 2
0 2
% info exists var(b)
1
Changing the default value only impact missing entries:
% info exists var(c)
0
% set var(c)
0
% array default set var 9999
% array default get var
9999
% set var(a)
1
% set var(b)
0 2
% set var(c)
9999
Default values do not show in entry lists or iterations:
% array names var
a b
% array get var
a 1 b {0 2}
% array for {key value} var {puts "$key => $value"}
a => 1
b => 0 2
% parray var
var(a) = 1
var(b) = 0 2
Unsetting the default value will only invalidate missing entries:
% array default unset var
% set var(a)
1
% set var(b)
0 2
% info exists var(c)
0
% set var(c)
can't read "var(c)": no such element in array
Setting the default value on a non-existing variable implicitly creates an array variable:
% array exists var2
0
% array default set var2 1234
% array exists var2
1
The default value can be set, modified or unset by trace handlers:
% set var(foo)
can't read "var(foo)": no such element in array
% trace add variable var read setRandomDefault
% proc setRandomDefault {name1 name2 op} {
upvar $name1 arr
array default set arr [expr {rand()}]
}
% set var(foo)
0.9132881643778124
% set var(foo)
0.6341786978925479
% set var(foo)
0.5976932461362766
% array default get var
0.5976932461362766
% proc setRandomDefault {name1 name2 op} {
upvar $name1 arr
array default unset arr
}
% set var(foo)
can't read "var(foo)": no such element in array
Implementation
The proposed implementation is available on branch tip-508 in the Tcl Fossil repository.
Copyright
This document has been placed in the public domain.