Playing Python

Richard Suchenwirth - Another little twister from the "Foreign languages series". Python ( http://www.python.org ) is another scripting language with the special feature that block structure is not expressed by bracing, but by depth of indentation. We can have that in Tcl too, if we ever feel like it. The following pytcl code (Tcl with slight approximations to Python)

py {
   proc fib {n}:
       set a 0
       set b 1
       while $b < $n:
          puts -nonewline "$b "
          foreach {a b} [list $b [expr $a+$b]] break
       #done

   for i in {foo bar baz}:
      if [regexp f $i]:
         puts -nonewline "!"
      puts $i
   set x 5
   if $x > 1 and $x < 10 or $x == 5 :
      puts Done-[fib 2000]
      puts OK.
}

gets translated to

   proc fib {n} {
       set a 0
       set b 1
       while {$b < $n} {
          puts -nonewline "$b "
          foreach {a b} [list $b [expr $a+$b]] break
       }
       #done

   }
   foreach i {foo bar baz} {
      if {[regexp f $i]} {
         puts -nonewline "!"
      }
      puts $i
   }
   set x 5
   if {$x > 1 && $x < 10 || $x == 5 } {
      puts Done-[fib 2000]
      puts OK.
      }

which is, of course, well-formed Tcl, by the following "preprocessor":

proc py body {
    set res ""
    set nbraces 0
    set olddent 0
    set dent 0
    foreach line [split $body \n] {
        if {[string trim $line]=="" && $res!=""} {
            append res \n
            continue
        }
        if [regexp {^([ \t]+)} $line -> dents] {
            set dent [string length $dents]
            if {$dent>$olddent} {
                append res " \{"
                incr nbraces
            } elseif $dent<$olddent {
                append res "\n$dents\}"
                incr nbraces -1
            }
        }
        if [regexp {(.*if) (.+) *:} $line -> pre cond] {
            regsub -all " and " $cond { \&\& } cond
            regsub -all " or " $cond { || } cond
            set line "$pre {$cond}"
        }
        regsub {:$} $line "" line
        regsub {while ([^\{]+)} $line {while {\1}} line
        regsub {for ([A-Za-z0-9_]+) in } $line {foreach \1 } line
        append res \n$line
        set olddent $dent
    }
    while {$nbraces} {
        append res "$dents\}\n"
        incr nbraces -1
    }
    lindex $res 0
}

Don't know what it's good for - fun, maybe?

One good thing though: braces around the while condition are automatically added (I still sometimes forget those...) Python's for .. in .. gets translated to foreach .. This could actually be done in the Tcl core: the second argument to for is the test, which is evaluated by expr, and expr will under no condition accept "in" as an argument - so that case could be rerouted to foreach (?)

Note also the last if statement - I like and and or better than their C equivalents (maybe worth a TIP? As multi-letter operators are being introduced with eq and ne string comparisons, the door is open to more "words"... )

But the absence of braces goes at the expense of muddying Tcl's simple word-based syntax with line and whitespace rules. Python uses braces only to mark dictionaries (arrays in Tcl), and with special syntax again:

tel = {'jack': 4098, 'sape': 4139}   ##Python, cf.
array set tel {jack 4098 sape 4139} ;## Tcl

Wonder how far Pytcl should go? Certainly not as far as emulating Python's way of exiting (one of the first things a novice learns in GvR's tutorial):

 import sys; sys.exit()

or their clumsy way of writing u"Hello\\u0020World", which in Tcl is just Hello\u0020World...

(that's spelled "Hello World" in Python, of course. Or u"Hello World", if it's important for you to use Unicode storage up front. Or u"Hello\x20World", if it's important for you to use hexadecimal escapes. Or u"Hello\u0020World", if it's important for you to use a Unicode escape. Or did you really want a string with a backslash, a "u", and four hexadecimal numbers? Why would anyone want that?).

RS: I picked \u0020 as a oversimple example - in practice it would be the code for a copyright sign, a Kanji, or so. What I meant is that in Python you have to decide beforehand whether it's a Unicode or an 8-bit string. And I was wrong about the double backslash in \\u - I thought it would be required :}


Another Python feature, copied over from C, is that function names in calls always have parens, even if called with no arguments, and the arguments are separated by commas, e.g.

foo(bar, baz) ## Python, C, ...
foo $bar $baz ;# Tcl

The former style can also be had in Tcl -- see Playing C.


What I want to be able to do in Tcl, that Python does very cleanly, is this:

class Foo:
   def __init__(self):
       self.a = 1
       self.b = 1

a = Foo()
a.a = Foo()
a.a.a = Foo()

print a.a.a.a

Simplistic, but that's the kind of thing tcl just doesn't do terribly well. And I'm not talking about the OO - I'm really just using that class as a struct. The important thing here is that the series of 'a's are references to a data container. They do not have to be explicitly destroyed, either - they will be garbage collected.

Lars H: Would Wish #72 on Tcl 9.0 WishList help with this? With it, the above (minus the OO) could become

typedef Foo list {a b} {Foo Foo}
proc Foo_init {n} {return [list $n $n]}
set a [Foo_init 1]
set {Foo a a} [Foo_init 2]
set {Foo a a a} [Foo_init 3]
puts ${Foo a a a a}

The value of $a would be

{{{3 3} 2} 1}

See also