Playing C

Richard Suchenwirth 2001-01-12 - Want C style? Get C style! Even after years, the flexibility of The chamaeleon language (Tcl) still amazes me. I have programmed in C for quite some time, but a post on comp.lang.tcl, asking why Tcl procs couldn't be called like in C, brought me to experiment with the following (which also helps to emulate Python - see Playing Python).

Here's a version of the "sum" proc (dead simple, just adding two arguments) that also accepts arguments in parens, separated by commas (and optional whitespace), and tries to look up variable names in the caller's scope (no dollars required!) - so you can call it in any of these ways:

        set y 4
        sum 17 4
        sum (17,4)
        sum (17,y)
        sum (17,$y)
 proc sum args {
        Cargs a b
        expr $a+$b
 proc Cargs args {
        upvar args uargs
        regsub -all { *, *} $uargs , uargs
        if [regexp {^\((.*)\)$} $uargs -> in] {set uargs [split $in ,]}
        foreach $args $uargs break
        set n 0
        foreach i $args {
            if [uplevel 2 info exists [list [set $i]]] {
                uplevel set $i [uplevel 2 set [set $i]]
            } else {
                uplevel set $i [list [lindex $uargs $n]]
            incr n 
        if [llength $uargs]!=$n {error "wrong # args: should be $args"}

One point to note is that you have to have whitespace between proc name and arguments... but then again, even that can be fixed by extending the unknown mechanism! This is all you have to do, if you want to allow parenthesized arguments directly after the command name:

 rename unknown _unknown
 proc unknown {args} {
    if [regexp {^(.+)(\(.+\)$)} $args -> cmd args ] {
        uplevel $cmd $args
    } else {eval _unknown $args}    ;# let old "unknown" do it

Then you might as well write:

 sum(1, 2)

Final note:

This all is not in the spirit of Tcl, but the spirit of Tcl includes doing things that are not in the spirit of Tcl. A philosophical problem? Conversely: try to get a C compiler (or Python interpreter) to accept

 sum 17 4
 sum $foo $bar

(Note: Try ipython. It does not require the parens.)

See also procc which allows to specify args and body of a proc in a subset of C... - C code generators

Here's what triggered this experiment:

Reinhold Kwauka <[email protected]> wrote in comp.lang.tcl:

 Is there any possibility to call a proc with parameters in a C-Style
 manner? What I try is
    proc sum { a b} {
       return [expr $ a + $b]
 Now when i call the proc i would like to do it like
    sum {$a $b}
 In this case Tcl think that one argument is missing, but for me 
 the source would be much more clearer.

Well.. Tcl is not C. In C, parens round function arguments are required even if there are no arguments. Tcl follows rather the shell style (Unix/DOS/VMS...), where the first word is the command name, the others the arguments, and all words separated by whitespace.

You can of course design your procs like you wish. For constant input, it's easy to use curly braces. The following example allows both styles (it accepts any number of arguments and uses either the first two, or the first two elements of a single argument ;-):

 proc sum {args} {
        if [llength $args]==1 {set args [lindex $args 0]}
        foreach {a b} $args break
        expr {$a+$b} 
 set x 17; set y 4

 sum 1 2          ;# (1)=> 3
 sum {1 2}        ;# (2)=> 3
 sum [list $x $y] ;# (3) => 21
 sum "$x $y"      ;# (4) => 21
 sum {$x $y}      ;# (5) => can't use non-numeric string as operand of "+"
 eval sum {$x $y} ;# (6) => 21
 sum $x $y        ;# (7) => 21

See? Braces have more meaning in Tcl than in C. Besides grouping their content into one word, they protect it from any evaluation - so expr tried to add the literal strings "$x" and "$y". If you want to pass in variables, and group them into one argument, you must use [list..] like in (3), or quotes like in (4) - if you are very certain that no whitespace is in the variable values. Using [eval..] in (6) has the same effect, and runs the same risk.

But to tell the truth, I think (1)=(7) is the easiest and nicest style...