Version 27 of Lambda in Tcl

Updated 2004-01-20 01:34:53

Richard Suchenwirth - Spent some time this weekend looking at other languages (Python, Lisp):

One of those thing is the anonymous function, or Lambda. Think of it this way: the three attributes of a proc are its name, its args, and its body. A lambda expression only has args and body and evaluates to a thingy that can be used like a proc's name. Concrete example: You have a list of data (e.g. prices) and want to make a list of results where some treatment was done to each element, e.g. add 16% VAT. Straightforward:

   set L {10 20 30 40 50}
   set res {}
   foreach i $L {lappend res [expr $i*1.16]}
   set L2 $res

Now if you want to wrap the single-element processing and the list processing in separate procs, you might say:

   proc addVAT {x} {expr $x*1.16}
   proc map {fun list} {
        set res {}
        foreach i $list {lappend res [$fun $i]}
        return $res
   }
   set L2 [map addVAT $L]

Now if you don't want to invent a proc name for each single-element proc, you're ripe for Lambda: given an arglist and a body, add a name, and make it a proc. One proposal: lambdas are anonymous - no name - empty string (which is a valid proc name in Tcl), so:

   proc lambda {argl body} {
        set name {}
        proc $name $argl $body
        return $name
   }
   set L2 [map [lambda x {expr $x*1.16}] $L]

This implementation has the feature that always the same name is used: a kind of garbage collection, but nested lambdas wouldn't work. If you want nonconflicting names for the lambda object, you might use "very telling names": use the line

        set name $argl/$body

which completely describes the function being defined, and will not leak if the same lambda is called more often. Luckily, the body is often very short in lambdas...;-)

On the other hand, in order to define a "proc with no name", or rather the empty string as name, we need not even define a lambda proc as above. Tcl's proc command returns nothing, which is the empty string, which happens to be the return value of lambda in the first alternative. So we may say

   set L2 [map [proc "" x {expr $x*1.16}] $L]

Now, if anyone slightens Tcl for not having a Lambda function, tell them it's called "proc {}" and was there all the time (just we didn't think of it...;-) You can make this lightweight lambda look nicer with

 interp alias {} lambda {} proc {}
 set L2 [map [lambda x {expr $x*1.16}] $L]

And then: Of course we can model all the functional programming goodies in Python (see also Steps towards functional programming), but there may be cooler ways: To add up the numbers in a list, you may

   set sum [reduce [lambda {p q} {expr $p+$q}] 0 $L]

(that's how it's done in Python, translated to Tcl, but:)

   set sum [expr [join $L +]+0]

does the same job and looks definitely slicker... (but see Elegance vs. performance for how slow it is) -- Richard Suchenwirth This was the init of a lengthy discussion on news:comp.lang.tcl briefly summarized here:

Larry Virden has Lambda-FAQ: http://www.purl.org/NET/Tcl-FAQ/part5.html

Donal Fellows presented a lambda namespace that never forgets ;-)

   namespace eval lambda {variable unique 0}
   proc lambda {argList body} {
       set name ::lambda::cmd[incr ::lambda::unique]
       proc $name $argList $body
       return $name
   }

and reported that reducing with join is much faster:

    % time {set sum [reduce sum 0 $L]} 10000
    781 microseconds per iteration
    % time {set sum [reduce [lambda {p q} {expr $p+$q}] 0 $L]} 10000
    867 microseconds per iteration
    % time {set sum [eval expr [join [concat 0 $L] +]]} 10000
    489 microseconds per iteration

Paul Duffin has lambda, and curry, in his Feather extension.

China Blue offers lambda, and map, without using proc inside:

    proc setp {vars vals} {
        set m [llength $vars]
        set n [llength $vals]
        while {$m>$n} {lappend vals {}; incr n}
        uplevel 1 [list foreach $vars $vals break]
        lrange $vals $m end
    }
    proc lambda {locals body args} {
        set args [setp $locals $args]
        eval $body
    }
    proc map {func list} {
        set r {}
        foreach e $list {lappend [eval $func $e]}
        return $r
    }

Later, some diversions of Common Lisp readmacros for Tcl, or a Tcl readmacro for Lisp. DejaNews has the details -- RS An in-depth document on Tcl lambda is at http://homepages.ihug.co.nz/~webscool/lambda.html


The life of a lambda proc can also be shortened with the method presented in Local procedures, where a proc is unset on the unsetting of a variable in the caller scope:

 proc lambda {args body} {
        set name lambda[intgen]
        uplevel 1 set __$name -
        uplevel 1 "{trace variable __$name u {rename $name {} ;#}}"
        proc $name $args $body
        set name
 }

with the old faithful numbering machine intgen making unique IDs:

 proc intgen {{seed 0}} {
      set self [lindex [info level 0] 0]
      proc $self "{seed [incr seed]}" [info body $self]
      set seed
 } ;# RS

Strangely enough, lambda can also be used to model data structures! Tcl and Lisp shows one implementation of lambda used to do Lisp's lists, with absolutely no data structure other than the lambda!


Re-thinking on Lambdas, I like the following best:

 proc lambda {args body} {
        set name [info level 0]
        proc $name $args $body
        set name
 }
 % set inv [lambda x {expr 1./$x}]
 lambda x {expr 1./$x}
 % $inv 5
 0.2

The name of the generated proc is exactly the command lambda was called with, a sort of Quines...(RS)


This version uses regexp and only works on one variable at a time. Enjoy.

 proc apply {body mylist} {
        set newlist {}
        foreach m $mylist {
            regsub -all {\!\!} $body $m cmd
            lappend newlist [eval $cmd]
        }
        return $newlist
 }

  set mylist {3 43 12 92}
  set newlist [apply {expr {2+!!}} $mylist]
  puts $newlist
  => {5 45 14 94}

  set mylist {fidel vlad paul}
  set newlist [apply {format "hello %s" !!} $mylist]
  => {hello fidel} {hello vlad} {hello paul}

Okay, the above implementations of lambda all look interesting. I have been using a variant of one of them in a commercial development effort (ah, all those once-off procs resurfaced as lambdas). I am collecting/writing/hacking a package of functional utilities in Tcl. These pages have inspired quite a few of them.

So, what would a production ready lambda look like? Apotential summary of some of the lambdas described here could yield:

 namespace eval ::lambda {}
 proc lambda {arglst body} {
     set level [info level 0]
     set name [string map {" " _ $ _  \; _ \" _ : _ \{ _ \} _ \[ _ \] _} $level]
     proc ::lambda::$name $arglst $body
     return ::lambda::$name
 }

The string map cleans up the lambda proc name so that it is eval safe.

-- Todd Coram


A generalized map, which takes one or more lists, is at Function mapping.


AM I have been reading a (bizarrely inaccurate) book on Python and I realised that Tcl is filled with lambda-like functionality:

  • Commands like [after] and [fileevent] take a script that can be viewed as an implicit lambda.
  • All call-backs in Tk are essentially lambdas.

Or am I mistaken? I would say that Tcl has an anonymous lambda feature :-)

RS: You might add interp alias to the list, which also comes close to lambda (see Custom curry). But "classical" lambdas as in Python, Lisp, or where else are characterized by the fact that you specify an argument list and a body, which may of course contain references to args in the arg list, and receive a (pointer to) a created functional object that behaves like specified. The variations on lambda above achieve that with little effort. Things to worry about (a wee little bit ;-) are:

  • garbage collection (could be done with ref-counting the generated proc object)
  • closure, lexical scoping (there we have a weak point in Tcl)

But after is just a feature-added eval, which is indeed the heart of Tcl (and Lisp..), but only half of the "Lambda story"...


CL wrote in the comp.lang.tcl newsgroup, on the question whether there are any practical uses for lambda: "Everywhere in Tcl you'd include a script as an argument, Pythoneers might consider a lambda.

One of the batteries base Python includes is Tkinter, a binding of Python to Tk. All Tk callback thingies are candidates to be lambdas:

    b = Button(text="Push me",
               command=lambda: sys.stdout.write("Pushed\n")) " 

[CL needs to make a point 'bout how the real action in Python is moving dramatically away from lambda and friends, and toward list comprehension.]


Peter Lewerin experimented with a Snit Lambda object.


CMcC is there any good reason proc returns "" instead of something more useful, e.g. the name of the defined proc, or the body, or even the compiled object? Seems like a waste of a good opportunity to me.


AM I think, no I am positive, that I have found an elegant solution to the problem of an anonymous lambda. It came to me after a weekend and a half of juggling various intricate solutions involving the unknown procedure. In the true spirit of Tcl, I stripped away anything that was redundant, sat down behind a computer and implemented the final version in say half an hour (including testing). Here it is - I do hope I have good enough an understanding of lambda, as it is quite a claim after the above discussion ...

 # Anonymous lambda 
 #
 proc lambda {list_args expression} {
    return [list LAMBDA $list_args $expression]
 }
 proc LAMBDA {list_args expression args} {
    foreach $list_args $args {break}
    return [eval $expression]
 }
 proc apply {fun args} {
    set result {}
    foreach a $args {
       lappend result [eval $fun [list $a]]
    }
    return $result
 }
 proc apply_double {fun1 fun2 args} {
    set result {}
    foreach a $args {
       lappend result [eval $fun1 [list [eval $fun2 [list $a]]]]
    }
    return $result
 } 

 puts [apply "string toupper" a b c d]
 puts [apply [lambda {x} {lindex $x 1}] {a b} {b c} {c d}]
 puts [apply_double [lambda {x} {string toupper $x}] \
                    [lambda {x} {lindex $x 1}] \
          {a b} {b c} {c d}]           

FW: This works fine in some ways, but you're making your own lambda construct that must be called via apply. One of the challenges is to make Lambdas callable like normal procedures, rather than invent a special procedure for managing them. Plus instead of using actual procedures, you're storing them as text and running them via eval, which, for one, doesn't compile to bytecode. Also, it doesn't retain procedure environment like [info args] etc.


[ Arts and crafts of Tcl-Tk programming | Steps towards functional programming | Category Data Structure | ]