Version 14 of Salvatore Sanfilippo

Updated 2003-11-10 10:14:03

Some kind of hacker (davidw was too kind with me in the last version of this page ;).

I like TCL, while there are a number of changes I propose (look forward on this page).

Why I like TCL?

For the following reasons: TCL is the glue able to combine data and code in a direct and expressive way, without tons of special rules, but with few basic ideas that works accross the whole language, in an orthogonal way. Also TCL doesn't force you to take the language as it is, you can extend TCL in itself, so eventually you start programming bottom-up like in lisp-like languages. In TCL like in Lisp it's possible to adapt the language to fit well the application you are writing, then to write the application in this new language. So after all I like TCL for the same reasons I like Scheme, but I tend to use they for very different tasks: when I need the ability to work a lot with strings, regexps, libarires, mix with C code, TCL is my way to go.

For what I use TCL?:

  • One-page scripts that do a lot of works with sockets, files, regexps, and so
  • Vertical programs with GUI that I use to sell to my customers
  • My own experiment with new interesting ways to use the server/client model
  • I'm starting to use it for web programming
  • Random hacking where all-is-a-string-and-i-like-to-eval-it comes handly

And of course I use it a lot when I think to major ideas in programming languages. TCL, like Lisp and SmallTalk, have something of special in the design.

I know TCL thanks to davidw, he used to show me the main ideas, and after some time I got in love of the language (at the point to write a TCL-like programming language that is nowaday just a dead piece of code into my HD). Since then I used many other languages with the same target, such Python, but I like much more the flexibility of TCL.

Other languages I like are: C, Scheme, SmallTalk, FORTH.

How to make TCL better in my opinion

In short:

  • Don't make math an exception in the language, expr should be just an optional,

but the core language should support math using commands like +, *, / and so on, with lisp-like semanthics so that

 [/ 1 2] => 1/2

Also native support for bignums seems to fit perferctly in TCL.

  • No implicit expr inside if, while, and so on.
  • Minimal TCL core, just what is needed to define the language. All the rest are libraries.
  • Support for references, see Tcl references in Tcl
  • A standard support for packet oriented protocols like UDP
  • Maybe some kind of macro system to try new ideas for the language in short time. Roy Terry, 31Oct2003: Tmac can do that in pure tcl code. I will be posting an update soon that does include examples of new language features such as enhanced switch and foreach command. Tmac - a Tcl macro processor package
  • No special-case types like arrays, but what I read about Tcl 9 is great! (dictionaries).

Random Stuff

The regmap function may be more secure (against malicious user input) and clean than the usual regsub+subst way, at least some time. Please tell me if something like this is already in some well known library.


Fles, a not complete SELF-like object system for Tcl

    # fles - a SELF-like object world for Tcl
    # Copyright (C) 2003 Salvatore Sanfilippo
    #
    # WARNING: not complete, just to show it to Tclers on the chat

    # TODO
    # destroy method
    # intiialize/cleanup methods (called on clone/destroy).
    #
    # It is better to leave the difference between 'share' and 'childof'
    # as a costructor operation?
    # Costructors of objects interested in copy of parents (on clone)
    # have just to call some 'cloneparent' procedure.
    # On the other hand with share/childof it can be simpler, and
    # it is much more easy to modify an object in order to change
    # a parent from share to childof and vice-versa.
    #
    # What about for multi-messages form?
    # Like:
    #   Rectangle x: 10 y: 20
    # instead of
    #   Rectangle x: 10
    #   Rectangle y: 20
    # But how to handle default arguments in methods this way?

    #################### DKF's code for proper quoting -- Begin ####################
    # Warning: possibly modified by me, extra errors are mine
    proc makeMap {} {
        set chars {{[\"$#; ]}}
        append chars \t
        set map [list \n \\n]
        foreach c [split $chars {}] {lappend map $c \\$c}
        return $map
    }

    variable addBackslashMap [makeMap]

    proc ' {string} {
        variable addBackslashMap
        string map $addBackslashMap $string
    }

    proc braceQuote {string} {
        list $string
    }
    ##################### DKF's code for proper quoting -- End #####################

    # String-concat its arguments.
    proc cat args {join $args {}}

    set ::objid 0; # objid 0 is reserved for the root object

    proc var {name def} {
        set ns [uplevel 2 set ns]
        # methods to set variables are evaluated in the context
        # of the slot owner, instead of the context of the object
        # receiving the message.
        namespace eval $ns [list variable __evalhere__$name 1]
        namespace eval $ns [list variable __evalhere__$name: 1]
        namespace eval $ns [list variable $name $def]
        namespace eval $ns "proc [' $name] {} {variable [' $name]; set [' $name]}"
        namespace eval $ns "proc [' $name]: val {variable [' $name]; set [' $name] [cat $ val]}"
    }

    proc method {name arglist body} {
        set ns [uplevel 2 set ns]
        namespace eval $ns [list variable __evalhere__$name 0]
        namespace eval $ns [list proc $name $arglist $body]
    }

    proc childof name {
        set ns [uplevel 2 set ns]
        namespace eval $ns "variable parents; lappend parents [' $name]"
        namespace eval $ns [list variable __cloneParent__$name 1]
    }

    proc share name {
        set ns [uplevel 2 set ns]
        namespace eval $ns "variable parents; lappend parents [' $name]"
        namespace eval $ns [list variable __cloneParent__$name 0]
    }

    proc obj {{name -} {slots {}}} {
        incr ::objid
        if {[string equal $name -]} {set name __anonymousObj__$::objid}
        set ns __obj__$::objid
        interp alias {} $name {} sendmsg $::objid
        interp alias {} [cat $ns ::self] {} $name
        interp alias {} [cat $ns ::parent] {} sendmsgParent $::objid
        namespace eval $ns [list variable self $name]
        namespace eval $ns [list variable selfid $::objid]
        namespace eval $ns {variable parents object}
        namespace eval $ns [list variable __cloneParent__object 0]
        namespace eval $ns $slots
        namespace eval $ns "proc __ns__ {} {return [' $ns]}"
        return $name
    }

    proc lookup {slot ns} {
        set x [namespace eval $ns "string length \[info proc $slot\]"]
        if {$x} {
            set args [namespace eval $ns info args $slot]
            set body [namespace eval $ns info body $slot]
            set evalhere [namespace eval $ns "variable __evalhere__$slot; set __evalhere__$slot"]
            lappend t $args $body
            if {$evalhere} {lappend t $ns}
            return $t
        } else {
            foreach parent [namespace eval $ns set parents] {
                set y [lookup $slot [$parent __ns__]]
                if {[llength $y]} {return $y}
            }
            return {}
        }
    }

    proc uplookup {slot ns level} {
        set x [namespace eval $ns "string length \[info proc $slot\]"]
        if {$x && $level <= 0} {
            set args [namespace eval $ns info args $slot]
            set body [namespace eval $ns info body $slot]
            set evalhere [namespace eval $ns "variable __evalhere__$slot; set __evalhere__$slot"]
            lappend t $args $body
            if {$evalhere} {lappend t $ns}
            return $t
        } else {
            incr level -1
            foreach parent [namespace eval $ns set parents] {
                set y [uplookup $slot [$parent __ns__] $level]
                if {[llength $y]} {return $y}
            }
            return {}
        }
    }

    proc sendmsg {id slot args} {
        set ns __obj__$id
        set x [namespace eval $ns "string length \[info proc $slot\]"]
        if {!$x} {
            #puts "recurse..."
            set slotimpl [lookup $slot $ns]
            if {![llength $slotimpl]} {
                error "No such method: '$slot'"
            }
            # Fixme, to eval in the namespace of the slot owner
            # pass just one argument, the namespace itself, and
            # dirctly call the proc by name instead to create
            # the fake _ proc. Behaviour is the same but should
            # be less slow.
            if {[llength $slotimpl] == 3} {
                foreach {a b ns} $slotimpl break
            } else {
                foreach {a b} $slotimpl break
            }
            namespace eval $ns [list proc _ $a $b]
            namespace eval $ns _ $args
            #catch {namespace eval $ns {rename _ {}}}
        } else {
            namespace eval $ns $slot $args
        }
    }

    proc sendmsgParent {id slot args} {
        set ns __obj__$id
        set slotimpl [uplookup $slot $ns 1]
        if {![llength $slotimpl]} {
            error "No such parent method: '$slot'"
        }
        # Fixme, to eval in the namespace of the slot owner
        # pass just one argument, the namespace itself, and
        # dirctly call the proc by name instead to create
        # the fake _ proc. Behaviour is the same but should
        # be less slow.
        if {[llength $slotimpl] == 3} {
            foreach {a b ns} $slotimpl break
        } else {
            foreach {a b} $slotimpl break
        }
        namespace eval $ns [list proc _ $a $b]
        namespace eval $ns _ $args
    }
    # The root object. It is a bit special so requires some hack.
    # Note that the root object works like a class, it hasn't
    # variables so can be shared between all the objects without
    # to be cloned.
    obj object {
        method print {} {self printto stdout}
        method printto {channel} {puts $channel [self tostring]}
        method parents {} {
            variable parents
            return $parents
        }
        method clone {} {
            set ns [self __ns__]
            set clone [obj]
            set cloneNs [$clone __ns__]
            foreach t [info vars [namespace current]::*] {
                set newname [cat :: $cloneNs :: [namespace tail $t]]
                if {[info exists $newname]} continue
                # puts "var : $t -> $newname"
                set $newname [set $t]
            }
            foreach t [info procs [namespace current]::*] {
                set newname [cat :: $cloneNs :: [namespace tail $t]]
                if {[llength [info procs $newname]]} continue
                # puts "proc: $t -> $newname"
                proc $newname [info args $t] [info body $t]
            }
            # Create the parents list, cloning the non-shared parents.
            set [cat :: $cloneNs :: parents] {}
            foreach p [set [cat :: $ns :: parents]] {
                if {[set [cat :: $ns :: __cloneParent__ $p]]} {
                    lappend [cat :: $cloneNs :: parents] [$p clone]
                } else {
                    lappend [cat :: $cloneNs :: parents] $p
                }
            }
            return $clone
            # TODO: remember to handle alises if needed.
        }
    }
    namespace eval [object __ns__] {
        variable parents
        set parents {}
    }
    # End of the root object prototype

    obj toaster {
        var crumbs 0
        method toast {nslices} {
            if {[self crumbs] > 50} {
                error "== FIRE! FIRE! =="
            }
            self crumbs: [expr [self crumbs]+$nslices*4]
        }
        method clean {} {
            self crumbs: 0
        }
    }

    obj smartToaster {
        method toast {nslices} {
            if {[self crumbs] > 40} {
                self clean
            }
            parent toast $nslices
        }
        childof toaster
    }

    if {0} {
    smartToaster crumbs: 4
    puts [smartToaster crumbs]
    puts [toaster crumbs]
    smartToaster toast 4
    }

    ################################################################################# Shapes example
    ################################################################################

    obj Shape {
        var x 0
        var y 0
        method moveTo {newx newy} {
            self x: $newx
            self y: $newy
        }
        method rMoveTo {deltax deltay} {
            self x: [expr {[self x]+$deltax}]
            self y: [expr {[self y]+$deltay}]
        }
    }
    obj Rectangle {
        childof Shape
        var width 0
        var height 0
        method tostring {} {return "A Rectangle at [self x],[self y], W:[self width]"}
    }

    puts [time {set r [Rectangle clone]} 100]
    Rectangle x: 10
    Rectangle y: 20
    Rectangle width: 50
    $r x: 100
    $r y: 150
    $r width: 50
    Rectangle print
    $r print

    Rectangle rMoveTo 5 5
    Rectangle print
    $r print

Category Person