TIP:		77
Title:		Support for Nested Paired Item Lists
Version:	$Revision: 1.3 $
Author:		Christian Williams <xian@planetoutpartners.com>
State:		Withdrawn
Type:		Project
Tcl-Version:	8.5
Vote:		Pending
Created:	07-Dec-2001
Obsoleted-By:	111
Post-History:	

~ Abstract

Tcl arrays can be transformed to and from lists using the ''array
get'' and ''array set'' commands.  This TIP proposes a new command for
working directly these paired lists, and extending them to allow
nesting in a manner analogous to [22].

~ Rationale

Tcl lists provide only ordinal access to their items; often it makes
more sense to access items by pre-assigned descriptive names.  This
can be easily accomplished with Tcl arrays.  Consider these
alternatives:

|  set urlList { http tcl.activestate.com 80 /index.html }
|
|  array set urlArray {
|       proto   http
|       host    tcl.activestate.com
|       port    80
|       uri     /index.html
|  }

Clearly the array approach promotes more readable code
(''$urlArray(host)'' versus ''[lindex $urlList 1]'').

However, it's quite unwieldy and sometimes expensive to use arrays to
access members of many sets of structured data, particularly when that
data contains nested structures.

Consider this structured data:

|  set data {
|       text    {ignored-data}
|       valid-styles {
|               justification {left centered right full}
|               font          {courier helvetica times}
|       }
|  }

Extracting items from structures like this can be accomplished by
multiple ''array set'' commands:

|  array set dataArray $data
|  array set validStylesArray $dataArray(valid-styles)
|  puts "Justification: $validStylesArray(justification)"

To modify an item in ''struct'', we need some pretty ugly code:

|  array set dataArray $struct
|  array set validStylesArray $dataArray(valid-styles)
|  set validStylesArray(justification) {left}
|  set dataArray(valid-styles) [array get validStylesArray]
|  set data [array get dataArray]

Clearly, all this setting and getting of arrays imposes a rather high
overhead; many variables are created and moved around.  Also, if this
is occurring in a loop, then care must be taken to unset the
''dataArray'' and ''validStylesArray'' arrays first.

In contrast, a C programmer may expect that code to look more like
this:

|  data->valid-styles->justification = 'left';

Extending Tcl with a command supporting nested, paired item lists
would permit very efficient and readable handling of these useful data
structures.

~ Specification

Under this proposal, a new command named ''pair'' (referring to the
pairs of name/value list items it works with) would be added to the
Tcl core.

A well-formed paired list is defined as a well-formed Tcl list whose
length is evenly divisible by two.  In each pair of list items, the
first item gives the name of the pair, and the second gives the value.
Paired lists may be nested by placing a valid paired list in the
second (value) item of any pair.  Note that the pairs are not grouped
together into a two-item list as in TclX's keyed lists.  Tcl's ''array
get'' command returns a well-formed paired list.

The syntax for the new ''pair'' command would be:

|  pair option variable node ?newValue?

Valid values for the ''options'' argument include ''get'', ''set'',
''unset'', ''exists'', and ''append''.  These subcommands are
equivalent to the existing Tcl commands of the same names.

The ''variable'' argument is the name of a Tcl variable; it is always
referred to by name, not by its value (that is, no ''$'').  Generally,
the variable would contain a well-formed, and optionally nested,
paired list.

The ''node'' argument is a well-formed Tcl list of zero or more items
specifying the route to the item we're interested in.

For example:

|  set data {
|       text    {ignored-data}
|       valid-styles {
|               justification {left centered right full}
|               font          {courier helvetica times}
|       }
|  }
|
|  puts "Justification: [pair get data {valid-styles justification}]"

displays "Justification: left centered right full".

If the ''data'' argument contains zero items, then the "root" node of
the variable is targeted -- that is, the entire variable:

|  pair set node {} new-value
|  puts $node

displays "new-value".

If a non-existent node is targeted using the ''get'' or ''unset''
options, an error is returned:

|  unset x
|  pair get x {first second third}
|  -> no such value

If a non-existent node is targeted using the ''set'' or ''append''
options, the node, and any parent nodes, are created.

|  unset x
|  pair set x {first second third} value
|  puts $x

displays "first {second {third value}}"

The ''exists'' option mimics Tcl's ''info exists'' command:

|  set x {name value}
|  pair exists x name
|  -> 1
|  pair exists x name2
|  -> 0

The ''set'' and ''append'' options return the value of the node that
has just been set, not the value of the variable.  This would seem to
be more in keeping with the intent of Tcl's ''set'' and ''append''
commands' return values than duplicating the exact behaviour:

|  puts [pair set x {first second third} value]

displays "value".

An error is returned if a variable is passed to the ''pair'' command
which doesn't contain a well-formed paired Tcl list at any point on
the way to the node specified by the ''node'' argument:

|  set x {name value thirdarg}
|  pair get x name
|  -> list must have an even number of elements

If there are traces registered on the variable passed to the ''pair''
command, they are triggered in the same manner as Tcl's ''set'' and
''append'' commands.  Note that the ''append'' option triggers only
write triggers, not read triggers.

Note that the ''set'' and ''append'' options both return the value of
the node specified, and the ''newValue'' argument is optional in both
cases, making the ''get'' option redundant.  The ''get'' command is
included to improve readability.

If the variable passed to ''pair'' doesn't exist, it will be created
if the option is 'set' or ''append''; the ''exists'' option will
always return a ''0''; the ''get'' option will return an error.

If a paired list contains multiple pairs with identical names, the
pair occurring later in the list is targeted.  This is specified to
mimic the behaviour of ''array set'':

|  set x "name value1 name value2"
|  pair get x name
|  -> value2
|
|  array set arrX $x
|  set arrX(name)
|  -> value2

~ Reference Implementation

http://sf.net/tracker/?func=detail&aid=491070&group_id=10894&atid=310894

There should be a public C API for working with nested paired lists.
The supplied reference code currently does not provide this.

~ Notes

It would be nice to mimic Tcl 8.4's new ''unset -nocomplain''
behaviour.

~ Side Effects

Whether the result of the pair operation is successful, the underlying
Tcl_Obj that represents the list argument may have its internal
representation invalidated or changed to that of a list.

~ Copyright

This document has been placed in the public domain.
