Version 0 of @decorators

Updated 2018-05-03 23:56:50 by kpv

Keith Vetter 2018-05-03: One feature I miss in Tcl that Python has is the easy ability to wrap functions. I've twice recently felt the need for this. The first time I wanted the same argument handling of Tip 176 i.e. being able to pass M+N. The second time when I wanted to memoize a function for dynamic programming.

Both of these can be solved by adding code to the start of your function--the memoize page has a slick function to do just that. But to my mind, a conceptually cleaner solution is to leave the function intact and just transform it by wrapping it.

In Python this transformation is done with decorators [L1 ] [L2 ], and it uses the eye-catching pie syntax:

@memoize
def my_function(a, b):
  return a+b

Once you start thinking of decorators, new uses keep coming up: insuring you're logged in, running only in test mode, timing functions, synchronization, automatic logging, etc. [L3 ] So I decided to see if I could write a Tcl version of a decorator for memoization and automatically processing arguments, ala tip 176. After some serious quoting issues and a good use of apply I came up with the following solutions.

proc @memoize {p pname pargs lambda} {
    if {$p ne "proc"} { error "bad synax: $p != 'proc'"}

    proc $pname $pargs "
        set cmd \[info level 0\]
        if {\[info exists ::MEM(\$cmd\)\]} { return \$::MEM(\$cmd) }
        set argVals \[lmap var {$pargs} {set \$var}]
        set ::MEM(\$cmd) \[apply {{$pargs} {$lambda}} {*}\$argVals\]
    "
}

# Test
@memoize \
proc test_@memoize {a b} {
    puts "in test_@memoize $a $b"
    return $a
}
test_@memoize 1 2
test_@memoize 1 2

proc @auto_index {p pname pargs lambda} {
    if {$p ne "proc"} { error "bad synax: $p != 'proc'"}

    proc $pname $pargs "
         set argVals {}
         foreach arg {$pargs} {
             set val \[set \$arg\]
             if {\[regexp \{^-?\\d+\[+-\]-?\\d+$\} \$val\]} { set val \[expr \$val\] }
             lappend argVals \$val
         }
         apply {{$pargs} {$lambda}} {*}\$argVals
    "
}

# Test
@auto_index \
proc test_@auto_index {a b c} {
    puts "a is $a and b is $b and c is $c"
}
test_@auto_index hello 3 4+5