Version 98 of Comparing Tcl with Python

Updated 2014-11-05 09:21:56 by plwork

LV wrote: Perhaps the community could contribute tips, references, etc. comparing and contrasting Python and Tcl to provide someone who programs in Python assistance in learning to read/write Tcl?


VK My vision of comparing is to start with a following road (feel free to change it and expand on each item in turn, shorten list or adding more items):

  • What are the basic data types? (hash/array/list/strings) Is everything an object? a string?
    • Python has all the Tcl data types, and has had the equivalent of dicts for some time. 'Everything is a hash table' is perhaps what you could say about how Python's object system works.
  • How is OO different between the two languages? Python has one true OO and is heavily based on it; Tcl has been shipping with TclOO since version 8.5/8.6, before it had many different types of OO as external libraries. What does this bring to language? advantages? disadvantages? (wiki has many about this; link to page would be good) Multiple inheritance is available in TclOO!
    • Python has OO, Tcl does as well. This means that all Python extensions can utilize and build on one OO system rather than having to deal with the fractured landscape that Tcl had. It will take a little time to consolidate the usage of OO features to the new TclOO.
    • Legolas It also means you do not have to select an OO system when you want to use OO for the first time, which used to be a nightmare in Tcl, but no more, thanks to DKF.
  • What is the typical usage of Tcl? of Python? of their combination? Is it Web, GUI, financial, physic computation? what?
    • Python is used for all kinds of things - possibly a little bit less for quick scripts, as it's certainly slower to write some things in Python than Tcl. Tcl's socket abstraction is, IMO, the best in the business for example.
  • IDE: Several GUI WYSIWYG have been written in Tcl (Visual Tcl, ...)
    • Aside from the very high quality ActiveState tools, I do not believe that any of the Tcl tools are maintained much. Python didn't have much until recently, but I believe there are a number of nascent systems that are fairly high quality.
  • IDE2: what debugger and ease of use tools are available?
  • What does it usually take to extend the functionality of an application? (as opposed to commonly used (mis?)conception that Perl programs are write-only)
    • Tcl's C API is more extensive, and IMO, nicer than Python's. It lets you access more of the underlying system.
  • What does it take to extend the language itself? Is it possible? on the Tcl/Python level? C level?
    • Tcl's extremely flexible syntax lets you add new control structures. You can't do that in Python.

      Since Tcl is merely a series of function calls, one can use Tcl's proc to replace or extend functionality, or to add new functionality.
  • OS coverage. Both support many platforms. Which coverage is wider?
    • Probably about the same. Tcl runs on some things like Cisco, although it had to be heavily modified to do that. Python runs on some things like Nokia cell phones: [L1 ]
    • Cross-platform deployment: Tcl has starpacks. Python has py2exe [L2 ] for Windows.
  • to be continued

Tcl, when it gets things right (sockets, event loop, regexps), has a higher level API that really hits a sweet spot. Python has much more functionality available to it, is very popular and is growing in popularity. Tcl's implementation is nicer in some ways. Things like i18n and threads tend to be 'done right', and are less intrusive than in Python.


Sarnold I have tried writing a small tutorial, while reading the beginning of the Python tutorial. I have abandoned it, because it was not the subject of this page.


Sarnold All right. In my personal experience, I always used Python for small tasks, so I am not the person who would comment it for large tasks.

I just noted there was overall more than 300 modules in the standard distribution, part of them being platform-specific.

About OO : Python has by no mean the simplicity of snit, nor the mixins, filters of XOTcl. The multi-thread stuff seems not to be as well handled in Python than in Tcl.

On the other hand, Python users can create new types with UserDict, UserList and so on.

I made some quick'n'dirty perf comparisons between ActiveTcl8.5a3 and Python2.4.1. The results show that Tcl is not so slow. Setting a variable, looping or sorting a list is done in quite the same time.


LV Okay - compare what comes in Python with ActiveTcl - what important functionality is missing? What could/should be added to ActiveTcl to bring them nearer to the same level of out of the box functionality?


SS I don't think we should compare against ActiveTcl because Python is distributed with all this modules inside the .tar.gz file. So it's not the case of a batteries included distribution. All the libraries are *part* of Python.

LV I understand. For things to be added to Tcl, a TIP needs to be written, argued, approved, and implemented. On the other hand, to add something to ActiveTcl, one need only convince the maintainer of ActiveTcl and find an implementation that uses TEA.

So it seems to me that changes to ActiveTcl might be more likely to occur in the short term.

And it still seems fair to me to compare the two - because ActiveTcl is a single file release of Tcl, which includes the tcl interpreter and extensions. And what one downloads from python.org is a single file download of Python, which includes the Python interpreter, and, if the person building it so desires, a series of extensions. However, the Python can be built without extensions.

For that matter, if someone wants to, feel free to compare the functionality of Python, with no extras configured, with Tcl out of the box.

The issue I'm trying to address is this - if someone who has to write some app considers Tcl vs Python to determine a language, how are things going to compare? Most importantly to me, What functionality is missing in Tcl?

davidw ActiveTcl is great as far as it goes (I've been very grateful for it when forced to use Windows several times), however, the vast majority of Tcl installations out there are not ActiveTcl, and people do take that into account when creating packages on top of Tcl.

RHS It's fair to say "Well, the base Python dist comes with all these packages. You have to go get the batteries included dist of Tcl to get the same". However, it's also fair to say "Well, the core dist of Tcl is lightweight, and you can't get a lightweight dist of Python" (or, would that be you have to go get the lightweight dist of Python? I don't know the answer). The idea is that it's a design philosophy, and the two languages made different choices. Just because one person holds that one of the choices is better does not mean that someone else might not feel the other way.

davidw Seeing as how Python is being distributed for cell phones of all things, presumably where there is a desire to cut it down, it's possible. Meanwhile in the non cell phone market, most people seem pretty happy to have a very capable tool rather than having to fetch a bunch of separate packages. When the less knowledgeable go to compare Tcl and Python, they may very well be comparing what they find on their Linux box, which is a cut down tclsh with no OO vs Python and all its packages.

SS Also maybe it's worth to note that Python's distribution is organized in a way that extensions are stored into a separate directory, and they don't share code with the Python's language core. It's like to add a directory 'extensions' in the Tcl tar.gz with popular C extensions, and let the Makefile to compile-install even those extensions, and not just the core language. So this does not affect in any way the ability to get a little Tcl install. It's also possible to disable from configure the compilation of some extension if desidered.

Sarnold IMHO it is highly desirable that the Tcl core has a small memory footprint ; perhaps should we add mmap, atexit and some compression formats that Python has.

Kevin Walzer Having just released a Python application that makes use of Tkinter, after releasing three or four Tcl/Tk programs, I can say that Python is a heavier language: probably more robust out of the box because of its standard library, but also more complex to work with (threads are almost essential for GUI programming; fortunately Python handles threading gracefully), and more complex to deploy in terms of standalone applications. Python's deployment tools (py2app on the Mac, and presumably py2exe on Windows) produce much larger applications, in part because Python and Tcl/Tk have to be bundled (if you are developing a GUI using Tk). Starkits and starpacks are simpler and smaller. On the other hand, there are some things that simply can't be done in Tcl that Python could do with either its core language or extension: for instance, an SFTP client can be done in Python, but not Tcl. So it's a matter of the right tool for the job.

DKF: [L3 ] is interesting because it throws up a list of things that people feel are wrong with Python when they're not engaged in a pissing war with Tcl. :-) In particular, many of the things that they feel are problems (e.g. threads, extensibility within the language, extensibility from foreign code (C), redistributability) are Tcl strengths. Given all that, I'm not too worried about things... :-)


AMG, hoping he doesn't get too far off topic: Lately I've been busy extending a Python program I wrote to read in a "Tcl-formatted" data file and spit out predigested hunks of C, C++, FORTRAN, and myriad custom formats to be read by a variety of programs on a variety of operating systems. It performs extremely well, and I'm very happy with it... except for the Python bit.

I wrote the first version in Tcl, and it was short and simple but no less powerful, but I was forced to rewrite it because the Engineering Review Board categorically said no to Tcl. The reason the Python version is just as powerful and easy to extend is that the input data is "Tcl-formatted", meaning that its syntax is precisely described by the Endekalogue sans substitution. It's pretty cool to be able to say "see the man page Tcl(n) for syntax" rather than having to write it out myself. Leveraging an existing standard whose documentation is already installed on nearly every Unix machine? What a concept. But on the downside, I had to write my own Tcl scanner; at over two hundred lines, this is the largest part of the whole program. (I did it as a state machine that reads one character at a time and never looks backward or forward.)

Just as in Tcl, semantics can differ from application to application; I have a different "commands" for defining airfields and for defining visual models. Similar to slave interps, the commands differ from context to context, e.g. "state" only fits inside a "switch" block, and "switch" only works at toplevel or inside "model", but the toplevel "switch" contains a few extra commands. That set of commands is defined in the Python script by classes, but those classes are really just Tcl commands in disguise. Inheritance is very shallow and only used to borrow methods that aren't being reimplemented, and the only important methods are set() and get(). A couple functions are used to manufacture classes with special properties, for instance the ability to handle basic types like integers and reals, accept multiple "arguments", be "called" multiple times, or have a default value. I believe GPS wrote a Tcl object system that works in much the same way, where procs can be used to augment the properties of objects. [add reference here]

On the Tcl side of the translation, everything is a string. I don't need to go into how nice this is. But on the Python side, type conversions are manual. Therefore set() reads in lists of strings analogous to command arguments, and get() returns the same data in a format Python can actually use. (set() is like a Tcl command invocation, and get() is for Python to get the invocation's return value in the right Python type.) The Python script then collates the data into whatever output format was requested on the command line. Since the "compilation" was so easy to write, I have time to make the output formats nice, comments and all. For example, the C output is ready to be fed into doxygen.

I tell you this story because it has given me cause to compare the two languages and to figure out how to make them work together. True, the "Tcl" in this case isn't really Tcl because it completely lacks substitution, but it does come close enough for my purposes. It's amazing how Tcl remains useful even after removing items from the Endekalogue! Seriously, the amount of code I had to write to interface the two languages shows their differences.

Tcl is very dynamic because "everything is a string" offers limitless flexibility. This is the #1 thing I miss when using Python. Here's a telling example: If I want to look up a variable by name, I have to say locals()[an_expr_that_evals_to_the_name]. At least I can do it, unlike in C. But the syntax is clumsy. Because the syntax is clumsy, Python programmers don't often consider "metaprogramming" approaches; they won't even think to loop over a list of variable names or components of names. Writing expressions that evaluate to names, while possible, is harder than simply duplicating the code and manually substituting in the correct names. I know; I was there. Lack of automatic type conversions also is quite a headache to me; 5 + 5 == 10, yet "5" + "5" == "55". As a Tcl user you may look upon this with disgust, but those of a different faith may think it to be quite marvelous. More on this in the next paragraph:

If one assumes that inheritance-based object orientation, enforced types, enforced structure, (enforced indenting!), and a procedural approach are the only way to go, then Python is a great choice. Take away those assumptions, and Tcl becomes an option. Not only does it become an option, but also it becomes a superior option. Let me explain why. Someone who is able to eschew any of the concepts I listed is someone who is capable of appreciating Tcl's strengths.

And the last thing I sorely miss in Python is functional programming. Sure, Python pretends to have functional programming, and it's certainly possible to do functional in Python, but the in-place semantics of lists and dictionaries ruin the whole thing for me. list.sort() returns None and changes list as a side effect. I have to explicitly make a copy, an operation that takes multiple statements: new_list = old_list[:]; new_list.sort(). I can't do all that as part of a single expression without implementing my own sort function.

In that last paragraph I mention one thing about Python that I do envy: None. See null for my reasoning.

Update: See grok for a Tcl version of the code I describe above.

Update #2: Null is a bad idea after all, and I don't want it anymore. ;^) The functionality offered by such a beast is more safely and reliably obtained through some out-of-band means, such as tagged data, checking for a variable's existence, or using a separate variable to track the "nullhood" for the first, where the definition of "nullhood" depends entirely on context.

(DKF: Null really does correspond to a non-existing variable or key. Literally. We implement it exactly like that.)

(aidanhs: just to note that Python has (since '04) the reversed builtin - new_list = reversed(old_list). The other in-place stuff was jarring though, having come from Erlang.)


iu2 I agree with all that, and I think too tcl is much more flexible. However, there are some features that make Python realy fun to use. One of them is just the "Python pretends to have functional programming" thing. I've written a whole page about it - Commands pipe.

List comprehensions are fun, so is Python's indentation (to my taste, many disagree) which eliminates the need to end blocks, and makes it feel like writing less.

Many places mention Python's clean syntax. Python's syntax is not about cosmetics, but rather about psychology - having a pleasant time coding. I also think that many coding patterns found in extensible languages like tcl have been imported to Python (up until version 2.5): Partial functions (tcl's commands prefix), with statement (like Common Lisp), list comprehensions (haskell), ternary operator, generators (provide a means of closure), lambdas. lambdas! How many pages are there trying to import lambda into tcl? What does a lambda give you? One line of code instead of three, I guess (a proc command..) Is it so much of importance? I say it is more psychological that practical, and I guess psychology is one of Python's engines. A good one.

And, of course, tcl got apply recently ;-)

I do prefer tcl over Python, though. I think syntax extension is a powerful thing, which puts behind other languages that don't have it.

AMG: Regarding the "pretend" functional programming: I wish the follow-through were a bit better. Lambdas are too weak because they can only contain expressions, and sorting cannot be done "functionally" without defining a wrapper function.

DKF: Perhaps this is a good point to show just how very flexible Tcl's syntax is by saying that you can have an indentation syntax for Tcl if you want. Also, Tcl's lambda terms (through the apply command) can have as much or as little state as you want; they've got exactly the same state rules as conventional Tcl procedures.


iu2: I would like an automated braces closing in an editor. Emacs does that to Lisp. You can open as many parentheses as you like and then pressing a hot key you close them all. On the same line. I think something like that in a tcl editor would be nice. One hot key press - and all the braces/brackets opened so far get closed on the same line. tcl code would then look like Lisp, but I don't think it's a bad thing. Those braces waste space, and I don't mind stuffing them as much as possible, as long as the editor is there for me.


My opinion is that Python is a better programming language than Tcl, but Tcl is a better metaprogramming language than Python. Since I usually seem to wind up doing metaprogramming, I prefer to use Tcl. Most programmers I know don't metaprogram, hence they prefer to use Python.

A good rule-of-thumb I've found (w/C++ programmers) is that if somebody enjoys writing template code in C++, they'll take to Tcl. If they shy away from templates, they'll prefer Python.


Kevin Walzer: One more point about Python vs. Tcl: if you are using Tk for a GUI, you can create much richer, more sophisticated GUI's from Tcl. I've done some work recently trying to get such cool Tk advances as Tile and Tablelist going in a Tkinter application (written using Python's Tk bindings), but I keep stumbling over obscure bugs. For instance, in Python/Tkinter, bindText (a BWidget command to bind an event to the selection of text in a tree or listbox with an image) returns no output. Beats me why it doesn't work. But I'm not good enough with Python to try and figure it out.

More on this at [L4 ].

Of course, this doesn't preclude you from using another GUI toolkit with Python, if you need Python for something that can't be done in Tcl. But I'm quite comfortable and productive with Tk, and see no reason to chuck all my investment of time in learning it; I will instead use it in the richest, most productive environment possible, from Tcl.

UPDATE: I've decided to abandon Python altogether, for reasons I outline at [L5 ].

Kevin Walzer: UPDATE AGAIN (3/13/2007): I may have spoken too soon about chucking Python. See [L6 ]. The core issue is summarized here: [L7 ].

LV (Totally off topic of this page) Some of what Joel writes makes sense. However, he oversimplifies things so that his point is made. He neglects, for instance, the most important reason to rewrite - the original code was WRONG, either due to wrong decisions, wrong coding, or changing environment. Another point which I felt was missed relates to the big mess - sometimes things are a big mess because lots of people have attempted to make fixes but didn't read the code, and so rather than fix things right, they just added on lots of junk which is irrelevant and which have made the code more fragile. Other reasons for messy code include people attempting to add in new, useless features, without having planned at all, by people insufficiently trained in programming. And last, and most common for me to see, is messy code because of changes in business rules by people who do not understand programming, who are not given guidance (or even better, direction) by someone who does know programming (and the program in question).

There are times when it is better to rewrite. But, the reason should not be "I don't understand the code" - because in all likelihood, the person after that will not understand the new code, either...

AMG, meandering off topic: Kevin, I've been there too. Look at OpenVerse. It's a quirky mess, but it works. The talented David Gale, drawing upon his XiRCON experience, successfully wrote OpenVerse in Tcl. The resulting code base looked awful, but the truth is that all code bases "look" awful, at least all code that has actually seen action. It takes a while (sometimes a very long while) to see the genius embedded within. Few have the patience, fewer can communicate their discovery to others, and practically nobody wants to listen.

Many people decided to rewrite it, and since many (myself included) felt that Tcl was responsible for the "messy" state of OV, most rewrites were in a different language. It's been rewritten in C, Perl, Python, and possibly others. None of these rewrites really went anywhere.

Second to the original, I think the Python version was the best*, but in the course of making it as featureful as the original it went downhill and became a genuinely unmaintainable mess, ultimately to be left incomplete and abandoned. And it wasn't nearly as usable as the original because it was a full screen SDL application lacking a real GUI. No widgets, just chat. Sure it looked cool, but could you pick avatars out of a menu? Could you share MP3s? Could you play any of the games? No, sorry. Those were just too hard to implement in the absence of Tk.

(* I should qualify that: it's the second-best graphical version. The best text-only version is ovcon, my quick hack in Tcl.)

As for myself, I incorrectly concluded that OV's problem was that it was written in Tcl, a very non-OO language, and that a visual chat program must be OO to work well**. I thought that the best thing to do would be to write a new version in Python and use wxWindows. I don't believe I ever actually wrote so much as a single line of code; I was too busy, and I wound up just contributing to the original version.

(** Seriously, I used to think that way! Many still do. But now I've come to love Tcl's "non-OO" status; what Tcl has is much better.)

A few years later I got a chance to test my theory. I had a school programming assignment, a networked game with a GUI, and I thought it would be great to do it in Python+wxWindows. After putting a bunch of work into it, I gave up in disgust. I had found that nothing fit. The threads trod all over each other and got in the way of network communications. The pieces didn't mesh when I tried to write the GUI code by hand, and when I used a GUI designer tool, the output was too alien for me to maintain and customize. The presence of "OO" only succeeded in establishing code bureaucracy. I wish I could remember more specifics, but this was a while ago. After aborting the Python effort, I hastily returned to Tcl and was able to write something much nicer and more functional without much effort. But by then I was quite late on the project. I eventually failed the class.

Trying again next year, I redid all the projects using Tcl, finished well ahead of schedule, always got A+, and passed with honors credit. How's that for a testimonial?

AL I write tcl a lot and python very little. However in august, I needed to take a look and customise some of the functions offered by python for creating a web server. And what I found was nice, clearly organised, well documented. Having used more than once tclhttpd, I would never, never, comment the tclhttpd code in the same way. If you look for a wiki syntax interpreter in python, you will find many, a simple tcl library does not exist to my best knowledge. We also do not have a nice tutorial/reference open source book as the python documentation. So we have some way to go to catch up Python.


iu2 Here is a little sample I wrote for both Python and tcl. I started in Python then moved to tcl, and it seemed to me that it is worth posting. The sample allows creating circles and squares in a canvas, and allows moving them around.

tcl

 package require Tk

 canvas .c -bg white
 pack .c

 frame .f 
 pack .f -expand 1 -fill both

 set gg(x) 0
 set gg(y) 0

 foreach a {square circle} {
    button .f.$a -text "Add $a" -command create_$a
    pack .f.$a -side left -padx 5
 }
 proc create_square {} {
    set tag [.c create rectangle 0 0 100 100 -fill blue]
    bind_shape $tag
 }
 proc create_circle {} {
    set tag [.c create oval 0 0 100 100 -fill red]
    bind_shape $tag
 }
 proc bind_shape {shp} {
    .c bind $shp <ButtonPress> [list record $shp %x %y]
    .c bind $shp <Button1-Motion> [list motion $shp %x %y]
 }
 proc record {tag x y} {
    set ::gg(x) $x
    set ::gg(y) $y
    .c raise $tag
 }
 proc motion {shp x y} {
    .c move $shp [expr {$x - $::gg(x)}] [expr {$y - $::gg(y)}]
    record $shp $x $y
 }   

Python

 from Tkinter import *

 root = Tk()

 can = Canvas(root, bg='white')
 can.pack(expand=1, fill=BOTH)

 fr = Frame(root)
 fr.pack(expand=1, fill=BOTH)

 def create_square():
    a = can.create_rectangle(0, 0, 100, 100, fill='blue')
    bind_shape(a)

 def create_circle():
    a = can.create_oval(0, 0, 100, 100, fill='red')
    bind_shape(a)

 def bind_shape(tag):
    coords = [0, 0]
    def record(evt):
       coords[0], coords[1] = evt.x, evt.y
       can.lift(tag)
    def motion(evt):
       can.move(tag, evt.x-coords[0], evt.y-coords[1])
       record(evt)

    can.tag_bind(tag, '<ButtonPress>', record)
    can.tag_bind(tag, '<Button1-Motion>', motion)


 for a, b in [['Add square', create_square], ['Add circle', create_circle]]:
    b = Button(root, text=a, command=b) 
    b.pack(side=LEFT, padx=5)

 root.mainloop()

Python uses the Tk interface. The canvas creation and shapes binding are quite the same. Python needs some more code to engage Tk. However, the real differences are: The looping through optional shapes (square, circle) is more elegant in tcl because of its 'code is data' property. That could be somehow achieved in Python but it wouldn't be as elegant. So the Python sample is more verbose in the shapes looping. Binding the shapes to events, though, is more elegant in Python. In tcl it is required to record the current coordinates into global vars, but the Python sample uses a closure (bind_shape), no globals. While this is more memory consuming, it is less prone to errors, and also easier to write, since the programmer doesn't need to go back to the beginning of the file to declare the globals. Perhaps that could be achieved in tcl using OO (I'm not familiar with any of tcl's OO libs), or with the 'current' tag using the canvas properties. However, I believe this emphasizes the elegance of a closure.

DKF: Tcl (as of 8.6a0) doesn't do closures, which has upsides too (closures make introspection trickier since they represent a namespace that can't be reached conventionally) but it does do OO through the inclusion of TclOO. See below for an instructive example:

package require Tk

canvas .c -bg white
pack .c

frame .f
pack .f -expand 1 -fill both

foreach a {square circle} {
    button .f.$a -text "Add $a" -command create_$a
    pack .f.$a -side left -padx 5
}

proc create_square {} {shape new [list rectangle 0 0 100 100 -fill blue]}
proc create_circle {} {shape new [list oval      0 0 100 100 -fill red]}
oo::class create shape {
    variable shape oldx oldy
    constructor scriptlet {
        set shape [.c create {*}$scriptlet]
        .c bind $shape <ButtonPress> [namespace code {my record %x %y}]
        .c bind $shape <B1-Motion>   [namespace code {my motion %x %y}]
    }
    method record {x y} {
        set oldx $x
        set oldy $y
        .c raise $shape
    }
    method motion {x y} {
        .c move $shape [expr {$x - $oldx}] [expr {$y - $oldy}]
        my record $x $y
    }
}

Of course, that's still doing some things I consider non-idiomatic, but you get the idea.

Scott Beasley: I know that not having a real built-in OO system in tickle is counted as a ding against it, but I like the fact that it doesn't. As we also know, these days we have several OO options, so picking the one you like I think is a better choice.

Two more examples of the above Shape drawing code:

Snit:

package require Tk
package require snit

canvas .c -bg white
pack .c

frame .f
pack .f -expand 1 -fill both

foreach a {square circle} {
   button .f.$a -text "Add $a" -command create_$a
   pack .f.$a -side left -padx 5
}

proc create_square {} {shape create rec%AUTO% [list rectangle 0 0 100 100 -fill blue]}
proc create_circle {} {shape create ovl%AUTO% [list oval 0 0 100 100 -fill red]}
snit::type shape {
    variable shape 
    variable oldx 
    variable oldy
     
    constructor {shapeparams} {
        set shape [.c create {*}$shapeparams]
        .c bind $shape <ButtonPress> [mymethod record %x %y]
        .c bind $shape <B1-Motion>   [mymethod motion %x %y]
    }
     
    method record {x y} {
        set oldx $x
        set oldy $y
        .c raise $shape
    }
     
    method motion {x y} {
        .c move $shape [expr {$x - $oldx}] [expr {$y - $oldy}]
        $self record $x $y
    }
}

Incr Tcl:

package require Tk
package require Itcl 3.4
namespace import itcl::*

canvas .c -bg white
pack .c

frame .f
pack .f -expand 1 -fill both

foreach a {square circle} {
   button .f.$a -text "Add $a" -command create_$a
   pack .f.$a -side left -padx 5
}

proc create_square {} {shape rec#auto [list rectangle 0 0 100 100 -fill blue]}
proc create_circle {} {shape ovl#auto [list oval 0 0 100 100 -fill red]}
class shape {
    variable shape 
    variable oldx 
    variable oldy
     
    constructor {shapeparams} {
        set shape [.c create {*}$shapeparams]
        .c bind $shape <ButtonPress> [itcl::code $this record %x %y]
        .c bind $shape <B1-Motion>   [itcl::code $this motion %x %y]
    }
     
    method record {x y} {
        set oldx $x
        set oldy $y
        .c raise $shape
    }
     
    method motion {x y} {
        .c move $shape [expr {$x - $oldx}] [expr {$y - $oldy}]
        record $x $y
    }
}

iu2 - DKF, getting back to the example, you said "Of course, that's still doing some things I consider non-idiomatic". I'll appreciate it if you please explain this in more detail, that is, what's non-idiomatic about the OO solution, and how you would write it to fit tcl idioms. Thanks a lot!

DKF: I'd rather make subclasses to handle the different shape types and avoid the create_circle and create_square commands, but these are not so important in the grand scheme of things. (It would also make the code less like the Python code it was emulating.)


Python quirks: http://www.lshift.net/blog/2009/10/29/python-quirks

Tcl quirks: (add your favorite inconsistency here)

DKF: Tcl has very few basic syntax quirk (we thought about adding one in 8.0 with unbraced expr, but didn't in the end). Some of the commands are fairly quirky still though. In particular, if is very peculiar (though thankfully intuitive) and exec is awful. I also really dislike variable, but others disagree with me. (There are other odd places, but they're less obvious.)

Semantically, there are quite a few places where behavior is just plain bizarre. For example, renaming a procedure from one namespace to another...


iu2 - I recently wrote an undo/redo mechanism for a tcl application, and I found lists manipulation a bit difficult: 'v' is a list of two elements. Each element is itself a list. I want to move an item from the beginning of the second list to the end of first list. This is the code snippet from my proc:

 if {[lindex $v 1] eq {}} {return empty}
 set item [lindex $v 1 0]              ;# get first item of second list
 lset v 1 [lrange [lindex $v 1] 1 end] ;# remove last item from second list
 if {$item != [lindex $v 0 end]} {     ;# avoid duplicates in first list
   lset v 0 end+1 $item                       ;# append it to the first list
 }
 return $item

Now, the python code is clearer:

 if v[1] == []: return
 a = v[1].pop(0)
 if v[0][-1] != a: v[0].append(a)
 return a

This is something regarding the syntax of the language, and I believe Lisp would also be less clear than the python example. But as in tcl there is the expr command for "clarifying" math operations, I think it might worth considering adding some kind of "clarifying" command to handle list expressions, say lexpr, or maybe adding a few more list commands so that the above example would be cleaner.

DKF: I wouldn't put two lists together like that normally (and instead would use two variables) but here's how you might do it:

 proc transfer {varName} {
     upvar 1 $varName v
     lassign $v v0 v1
     if {![llength $v1]} {return}
     set a [lindex $v1 0]
     if {$a ne [lindex $v0 end]} {lappend v0 $a}
     set v [list $v0 [lrange $v1 1 end]]
     return $a
 }

The order of operations is slightly different (or you could use lvarpop from TclX for more similarity) but overall the same operation happens.

PL 2014-11-05: in a Tcl that has the lassign command the relevant lines become

     set v1 [lassign $v1 a]                       ;# a = v[1].pop(0)
     if {[lindex $v0 end] ne $a} {lappend v0 $a}  ;# if v[0][-1] != a: v[0].append(a)
     set v [list $v0 $v1]

But, yeah, it's convenient to be able to operate on list items. If one could dream:

# NOTE : NOT actual Tcl code
 proc transfer {varName} {
     upvar 1 $varName v
     if {[lindex $v 1] eq {}} {return empty}
     upvar 0 [nth $v 0] v0 [nth $v 1] v1         ;# creates local aliases for items in v
     set v1 [lassign $v1 a]                      ;# changes v1, v, and the variable in caller's frame
     if {[lindex $v0 end] ne $a} {lappend v0 $a} ;# changes v0, v, and the variable in caller's frame
     return $a
 }

Alternatively, one could stop dreaming and use TclOO to add the functionality with minimal effort:

oo::class create TransferList {
    variable v0 v1
    constructor v {lassign $v v0 v1}
    method get {} {list $v0 $v1}
    method v0 {} {set v0}
    method v0.append a {lappend v0 $a}
    method v0.last {} {lindex $v0 end}
    method v1 {} {set v1}
    method v1.pop {} {set v1 [lassign $v1 a] ; set a}
}

proc transfer v {
    if {[$v v1] eq {}} {return empty}
    set a [$v v1.pop]
    if {[$v v0.last] ne $a} {$v v0.append $a}
    return $a
}

set v [TransferList new {{a b c} {d e f}}]
list [transfer $v] [$v get]
# => d {{a b c} {e f}}
$v destroy ; set v [TransferList new {{a b c} {}}]
list [transfer $v] [$v get]
# => empty {{a b c} {}}
$v destroy ; set v [TransferList new {{a b c} {c d e}}]
list [transfer $v] [$v get]
# => c {{a b c} {d e}}

apmay - 2013-04-07 13:58:31

Another way to transfer an item from the beginning of one list to the end of another.

proc lpop {listname args} {
  upvar $listname lst
  if {![info exists lst]} { return "-1" }
  set item [lindex $lst $args]
  set list_path [lrange $args 0 end-1]
  set i [lindex $args end]
  lset lst $list_path [lreplace [lindex $lst $list_path] $i $i]
  return $item
}

proc transfer {varName} {
  upvar $varName v
  if {![llength [lindex $v 1]]} {return}
  set a [lpop v 1 0]
  if {$a != [lindex $v 0 end]} {lset v 0 end+1 $a}
  return $a
}

RickH - 2014-02-18 06:03:36

lset v 0 end [expr {[lindex $v 0 end] ne [lindex $v 1 0] ? [lindex $v 1 0] : [lindex $v 0 1]}]
return [lindex $v 1 0]

AMG: Would you mind explaining what this code does and how it is useful?


Tcl vs Python: