** What's "with" ** '''"With"''' is a construct that can automatically clean up "garbage" after finishing a task. It comes from [Lisp]. In Tcl it is used in one of two ways: `with-new-foo name script` or `with-existing-foo foo script`. In the former case the command `with-new-foo` creates a foo, sets the caller's variable `name` to the new foo through [upvar], runs the script, and then destroys the foo. In the latter case the command only runs the script and destroys the foo afterwards. It is up to the user to acquire a foo first. For example, [open] is usually combined with a [chan close]. A "with" construct will automatically close the channel (file descriptor). See [withOpenFile] and [Category With Pattern]. Reasonable use of the "with" construct can make your code visibly clearer. ** An implementation ** ====== proc getFinalByOp {op opret} { switch -exact -- $op { open { return [list chan close $opret] } default { return [list] } } } # # op: a command # opargs: @op's arguments # body: code that will be executed # ?_finally?: provide a 'finally' yourself # ?varname?: result of @op # proc with {op opargs body {_finally {}} {varname handle} } { set finally $_finally try { set [set varname] [$op {*}$opargs] if {$finally eq {}} { set finally [getFinalByOp $op [set [set varname]]] } eval $body } finally { eval $finally } } ====== This implementation only supports [open], it's not extensive, and it's buggy (see Discussions below). *** How to use it *** ====== with open {a.txt w} {chan puts $handle "hello world"} with open {a.txt r} {puts [read $fd]} {} fd with puts {"a test"} {set a {hello}} {puts $a} ;# a meaningless example ====== *** A more complex example *** Read in a file (employees.txt) of the following format, ====== name1,salary1 name2,salary2 name3,salary3 ====== The real file is, ====== Mike Foo,10000 Jack Bar,2000 John Doe,3000 ====== If salary < 3000, add 200, and in the end print the result, write it back. A possible implementation (can be easier?), ====== # ... "with" here ... with open {employees.txt r+} { set contents [chan read -nonewline $handle] foreach line [split $contents \n] { set list [split $line ,] if {[lindex $list 1] < 3000} { lset list 1 [expr {200+[lindex $list 1]}] } lappend result "[lindex $list 0],[lindex $list 1]" } chan seek $handle 0 chan truncate $handle 0 puts [join $result \n] chan puts -nonewline $handle [join $result \n] } ====== ** Discussions ** From the IRC channel, ====== I am afraid that 'general with' will not work - not with bodies that try to use variables from the calling env ... nor with commands that resolv differently in the calling env and in the proc's namespace ====== cyril: I have a much simpler implementation https://github.com/cyrilthomas/with <> Control Structure | Concept | With Pattern