TIP: 212 Title: Temporarily Opening out a Dictionary State: Final Type: Project Tcl-Version: 8.5 Vote: Done Post-History: Version: $Revision: 1.9 $ Author: Donal K. Fellows Created: 11-Aug-2004 Keywords: tcl, dict, update, script ~ Abstract This TIP proposes a new subcommand of '''dict''' that associates variables with dictionary entries while a Tcl script (the "body" of the command) runs. ~ Rationale The standard set of operations provided by the '''dict''' command (see [111] and [163]) give a fairly rich set of operations, but there are always further things that script authors want to do. Instead of ever-expanding numbers of subcommands offering tiny variants on existing operations, this TIP provides a generalized mechanism for associating variables and dictionary entries for the duration of a script. This makes writing arbitrary complex manipulations of dictionaries much simpler. ~ Proposed Change This TIP proposes two new subcommands to '''dict''', '''update''' and '''with''', with the following syntaxes and semantics. ~~ The 'dict update' Subcommand > '''dict update''' ''dictVarName dictKey varName'' ?''dictKey varName'' ...? ''bodyScript'' In prose, the first argument is the name of a variable containing a dictionary that will be updated, the last argument is a script to be executed with the variables set up and whose effects are expected to be to update the associated variables, and the other arguments between those two (of which there must be an even number, with a minimum of two) are a list of dictionary keys and the names of variables with which to associate those keys. The semantics of the command are this: 1. For each dictionary key (''dictKey''), the value from the dictionary contained in the variable named by ''dictVarName'' which that key maps to will be written to the variable associated with that key (''varName''). If the key is not present in the dictionary, the variable will be unset. 2. The script (''bodyScript'') is executed. 3. The dictionary is read out of ''dictVarName'' again and, for each ''dictKey'', the ''varName'' variable is consulted and the contained value placed in the dictionary (if ''varName'' is unset, any existing mapping for ''dictKey'' will be removed.) Finally, the resulting dictionary value is written back to ''dictVarName''. > If the ''dictVarName'' variable was unset (without being set to a new value) or otherwise rendered unreadable during the execution of ''bodyScript'', the updating of the keys and writing back of the resulting dictionary to ''dictVarName'' is skipped. Note that it is only an unreadable ''dictVarName'' that is treated this way; it is an error for the variable to contain a value that is not a valid dictionary. Note that ''dictVarName'' is read twice (each time, the value read must be a valid dictionary or the variable must be unset, which is interpreted as an empty dictionary) and (assuming the second read succeeds and returns a valid dictionary value) written once, and that ''no'' traces are set up; ''dictVarName'' is completely independent of the ''varName''s during the execution of ''bodyScript''. Also note that step 3 is carried out even if ''bodyScript'' completes with a result code other than TCL_OK, and that the result code of the overall command will be the result code of the script unless TCL_ERROR was the result of the write-back stage, which might happen if the variable ''dictVarName'' is updated (during the execution of ''bodyScript'') to contain a non-dictionary value, or if the variable ''dictVarName'' is either unset or cannot be written to (which can happen if a write trace throws an error); note that this mandates that any errors from the ''bodyScript'' are lost if the write-back phase also throws an error. If the result code is TCL_OK, the result string/object will be whatever the result string/object of ''bodyScript'' was. ~~ The 'dict with' Subcommand > '''dict with''' ''dictVarName'' ?''key ...''? ''bodyScript'' This is a shorthand version of '''dict update''' (named after the Pascal ''with'' keyword) which maps ''all'' of the keys in the dictionary in the variable ''dictVarName'' to variables during the evaluation of ''bodyScript''. Note that it builds a list of all the keys in the dictionary before ''bodyScript'' runs and only those variables which are created are mapped back at the end; if extra keys are defined in the dictionary in the variable during the execution of ''bodyScript'', they will be left alone when ''bodyScript'' finishes. The variables will be written to and read from in the natural order of the keys of the dictionary in the variable ''dictVarName'' when the '''dict with''' command starts. When a ''key'' (or several ''key''s) is provided, it describes a path into a nested set of dictionaries (much like with '''dict set''') where the selected leaf indicates a dictionary that is to be opened out. When writing back, non-existence of any key on the path is treated the same as if ''dictVarName'' itself didn't exist. ~ Examples A somewhat-less-efficient version of '''dict set''': | dict update someVar $key local { | set local $value | } Sorting a list inside a nested dictionary: | dict update someVar OuterKey temp { | dict update temp InnerKey temp2 { | set temp2 [lsort $temp2] | } | } "Renaming" a key: | dict update someVar oldKey foo newKey bar { | set bar $foo | unset foo | } Opening out a nested dictionary: | dict update someVar OuterKey temp { | dict with temp { | # Some script goes in here... | } | } | # Alternative version using a key with 'dict with'... | dict with someVar OuterKey { | # Some script goes in here... | } ~ Reference Implementation See SF patch 1008768[http://sf.net/tracker/?func=detail&aid=1008768&group_id=10894&atid=310894]. ~ Copyright This document has been placed in the public domain.