Version 8 of Pipeline programming

Updated 2007-03-14 01:17:21 by btheado

SS 13 March 2007. I like to program in a functional style, so many times I end writing in Tcl and other languages something like [a [b [c [d $arg]]]]. Semantically this is like the Unix shell pipe, so I wrote the following function.

Example:

 set s "    supercalifragilistichespiralitoso    "
 pipe    {string trim $s} {split * {}} {lsort -unique} {join * {}} > s \
         {string length} > l
 puts $s
 puts $l

Will output

 acefghiloprstu
 14

The code:

 proc pipe args {
     set r [uplevel 1 [lindex $args 0]]
     for {set i 1} {$i < [llength $args]} {incr i} {
         set e [lindex $args $i]
         if {[llength $e] == 1} {
             if {$e eq {>}} {
                 incr i
                 uplevel 1 [list set [lindex $args $i] $r]
             } else {
                 set r [uplevel 1 [list $e $r]]
             }
         } else {
             set cmd {}
             set substdone 0
             foreach arg $e {
                 if {$arg eq {*}} {
                     lappend cmd $r
                     set substdone 1
                 } else {
                     lappend cmd $arg
                 }
             }
             if {$substdone == 0} {
                 lappend cmd $r
             }
             set r [uplevel 1 $cmd]
         }
     }
     return $r
 }

 set s "    supercalifragilistichespiralitoso    "
 pipe    {string trim $s} {split * {}} {lsort -unique} {join * {}} > s \
         {string length} > l
 puts $s
 puts $l

If you read italian there is a longer article about this concept in my blog at http://antirez.com/post/49


Brian Theado - Nice! I have a slightly different version I use sometimes. Unlike yours it only works for commands that return other commands (i.e. jacl, tcom and tdom are all good candidates). Also, it actually uses a | character as the separator.

 proc pipe {cmd args} {
   while {[llength $args]} {
        set n [lsearch -exact $args |]
        if {$n < 0} { set n [llength $args]; lappend args | }
        set a [lreplace $args $n end]
        if {[llength $a]} {
            set cmd [eval [linsert $a 0 $cmd]]
        }
    set args [lreplace $args 0 $n]
    }
    return $cmd
 }

I adapted the above code from some version of ratcl (my all-time favorite Tcl extension). It uses the "|" to chain together view operators.

Examples

tdom:

 pipe [dom parse $xml] documentElement | firstChild | getAttribute style

 # Instead of:
 set document [dom parse $xml]
 set root     [$document documentElement]
 [$root firstChild] getAttribute style

tcom:

 set excel [::tcom::ref createobject Excel.Application]
 set worksheets [pipe $excel Workbooks | Add | Worksheets]
 set lastWorksheet [$worksheets Item [$worksheets Count]]

 # Instead of:
 set application [::tcom::ref createobject "Excel.Application"]
 set workbooks [$application Workbooks]
 set workbook [$workbooks Add]
 set worksheets [$workbook Worksheets]
 set lastWorksheet [$worksheets Item [$worksheets Count]]

Category Command