Version 8 of Functional composition

Updated 2001-12-12 12:08:10

Richard Suchenwirth - More steps towards functional programming... When reading about, and playing Haskell, I met the concept of functional composition again, where you conglomerate some functions into one, so in pseudocode the application of the functional object (f . g) to a variable x

 (f . g)(x)

is equivalent to

 f(g(x))

This, of course, can again be had in Tcl - we just have to compromise that the infix operator "." as used in Haskell must be moved to prefix position (before the arguments), as Polish notation requires - but this allows us to pass any number of arguments, not just two. Also, considering that this code should be runnable in a wish, "." is a (maybe the only) taboo name for a proc: defining "proc ." destroys the primary toplevel window, often causing wish to exit. So I changed the name to the slightly bigger, but harmless comma: ",".

The code below will create a new procedure, which needs a name. I have first found out in Lambda in Tcl that [info level 0] makes a nice self-referential name: it tells how the "," proc was called. As opposed to just counting up a lambda index, this has the added advantage that it avoids memory leaks - execute the same code as often as you wish, the name is always the same and thus overwrites the previous instance...

For now I assume that the generated procedure takes a single argument (call it "x") - but that could be a list. However, the functions to be nested are not restricted to a single word. Rather, if you brace (or quote) them, they may have more leading arguments (in technical terms, currying), as demonstrated by the "string toupper" example below. In Tcl's glorious spirit of "everything is a string" (so don't be afraid of nothing ;-), we just build up a well-formed proc body (the closing braces were factored out to prevent over-long lines), declare that with the fancy name and the boring "x" argument, return the name, and off we go...

 proc , args {
    set   name    [info level 0]
    set   closing [string repeat {]} [expr [llength $args]-1]]
    set   body   "[join $args { [}] \$x $closing"
    proc $name x $body
    set   name
 }

if 0 {Now testing... both use cases of "on-the-fly", and saving the generated name in a variable for subsequent use: the first example is strictly alphabetic, while in the second, "T" comes before "c" and" "l" - where it belongs ;-}

 % [, lsort {string toupper}] {z R a}
 A R Z
 % set try [, {string tolower} lsort]
 , {string tolower} lsort
 % info body $try
 string tolower [lsort $x ]
 % $try {l c T}
 t c l

if 0 { Final note: Reading on Haskell, I sometimes have a vague feeling of inferiority: They have so much (at a cost of increased syntax complexity, types and all) - but then again, our simple Tcl can catch up in 7 lines of code...


Reinhard Max contributed this condensed version:

 proc � {args} { 
  proc [set name [info level 0]] x "[join $args { [}] \$x [string repeat \] [expr {[llength $args]-1}]]"
  set name 
 } 

(RS)...or this one-liner:-)

 proc , args {append _ [proc [set _ [info level 0]] x "[join $args { [}] \$x [string repeat \] [expr {[llength $args]-1}]]"]}

Arts and crafts of Tcl-Tk programming}