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,
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  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