Version 12 of half-lambda

Updated 2007-10-24 13:45:21 by jdc

if 0 {Richard Suchenwirth 2004-04-22 - A lambda is an anonymous function described as a pair argl body, just as a named function can be described as a triple name argl body (that's the arguments to Tcl's proc), or as a pair name lambda. The argument list argl is matched with the actual arguments at call time. However, there is a certain arbitrariness in the contents of argl due to the selection of formal argument names, which leads to the question whether

 lambda x {expr $x*$x}, and
 lambda y {expr $y*$y}

are the same lambda, or different. Reading about TRAC, I found that the creation of the body, and half of the argument assignment happen in two steps there:

 #(DS,square,(#(mu,x,x)))
 #(SS,square,x)
 #(CL,square,5)'25

The first line defines a "form" (a string, which can serve as variable or function body), while the second line rewrites the form by replacing all occurrences of "x" with the non-character \1\ (more arguments would be rewritten to \2\, \3\,...). The string value of square is now #(mu,\1\,\1\).

In the third line, the form is called, and the second half of the argument assignment takes place: all occurrences of \1\ in the form are replaced with the first argument, "5", and the multiplication primitive #(mu,5,5) is evaluated, returning 25. This sounds vaguely familiar to Tcl'ers, yet strange. But we can join the DS and SS steps into one by just using the argument index as name, so the definition of a "form" in Tcl is just

 set square {expr {$1*$1}}

which relieves us of the argl body dichotomy, and the arbitrariness mentioned above - some discipline in argument names (integers corresponding to their position in the argument list, starting from 1) allows us to have a single string as full definition of an anonymous function, the body only. That's what I half-jokingly call a "half-lambda".

The idea is not new, of course - Unix and DOS shells, as well as awk and SQL functions, have been using such numeric parameter names for a long time. In a different vein, RPN languages can do without argument names by using stack operations instead (see RPN again) - the corresponding half-lambda in RPN would be {dup mul}. In Playing bytecode, a different flavor of RPN, with numbered arguments again, would have the square example come out as "1@d*", probably one of the shortest ways to write a square function.

What's only left to be done is to implement the second half of the assignment, with the actual arguments, but that's easy:}

 proc call {form args} {
    set i 0
    foreach arg $args {set [incr i] $arg}
    eval $form
 }
 set square {expr {$1*$1}}
 % call $square 5
 25

if 0 {Alternatively, if you have an integer range generator handy:

 proc call {form args} {
    foreach [range 1 [llength $args]] $args break
    eval $form
 }
 proc range {from to} {
    for {set i $from; set res {}} {$i<=$to} {incr i} {lappend res $i}
    set res
 }
 % call {expr $1*$1} 5
 25
 % call {expr hypot($1,$2)} 3 4
 5.0

Argument names of course have their good sides too, as they can document the purpose of an argument, so I'm not going to rewrite my Tcl code base in terms of "forms". But exploring different ways is always fun with Tcl... :)

RS 2007-10-24: Afterthought - now that we have apply in Tcl 8.5, the call could now be called apply/2 ... :^)