Updated 2014-01-13 17:16:46 by PeterLewerin

What's "with"  edit

It's a construct that can automatically clean up "garbage" after finishing a task.

For example, open is usually combined with a chan close. A "with" construct will automatically close the file descriptor (aka. handle). See also withOpenFile.

Reasonably use the "with" construct can make your code visibly clearer.

One possible implementation  edit

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  edit

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  edit

Read in a file (employees.txt) of the following format,

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  edit

From the IRC channel,
<miguel> I am afraid that 'general with' will not work - not with bodies that try to use variables from the calling env
<miguel> ... 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