Version 0 of bifurcation

Updated 2004-07-09 21:38:43

Page started by Lars H, 9 July 2004.

For some time now I've been toying with an idea concerning Tcl and functional programming. Recursion is the fundamental concept in functional programming (in the purer installations it is even the only way to handle iteration), but recursively heavy algorithms are usually not a good idea in Tcl, because the cost of recursion is comparatively large, and certainly larger than in the average functional language. One interpretation of this is that Tcl is useless for functional programming, but since I don't believe in that my conclusion must instead be that straightforward recursion is in Tcl a too primitive control structure to be the single solution to all problems. There has to be other things that can do better; the only problem is figure out what they are!

Tail call optimization is one thing. Even languages with low cost for recursive calls tend to suck if they haven't got this built in; it is what pure functional languages use to achieve decent loop performance. But perhaps it is, for practical purposes, rather a distinct control structure which can be dressed out as a recursion? Consider the following command

 proc iterate {vars vals body} {
    while {![
       catch {
          uplevel 1 [list foreach $vars $vals break]
          uplevel 1 [list if 1 then $body]
       } vals
    ]} {} ; # Empty while body
    return $vals
 }

Syntax-wise it is like foreach, but it only uses the given list of values on the first iteration. On the second iteration, it uses the result from the first iteration, on the third iteration it uses the result from the second iteration, etc. However if one uses the return command in the body then the iterate command returns with that result (break would have been more natural, but that does not support returning a value).

To see how this can be equivalent to a tail recursion, consider the following tail call optimizable implementation of the factorial function.

 proc factorial1 { n { result 1. } } {
    if {$n <= 1} then {return $result}
    factorial1 [expr {$n-1}] [expr { $n * $result }]
 }

The corresponding implementation using iterate is

 proc factorial2 {n} {
    iterate {n result} [list $n 1.] {
       if {$n <= 1} then {return $result}
       list [expr {$n-1}] [expr { $n * $result }]
    }
 }

Is factorial2 more natural than factorial1? That is probably to equal parts a matter of custom and personal taste. For things usually described as functions (e.g. the factorial) the factorial1 style is natural, but for things more commonly described as sequences (or a family of sequences) the factorial2 style has a point.

In terms of speed, and despite what was said above concerning recursion overhead, factorial1 wins by roughly a factor 5. Furthermore if one removes the seemingly pointless "if 1 then" (which is there to force byte-compilation of $body) from the definition of iterate then it will be more like a factor 20! From that one can learn that the overhead of a script level implementation of this kind of control structure is generally much larger than the overhead of recursion, so speed comparisons are probably uninteresting unless one reimplements the control structure commands in C. I'll leave the speed issue at that (unless someone jumps in to show how to make a Critcl implementation of this kind of command). Besides, the fastest Tcl implementation of the factorial is anyway the straightforward iterative one, not any functional programming variant.

iterate is however only a warm-up, the main topic here is bifurcation. A problem with tail call optimization is that there must not be any post-processing of the tail call result, whereas most naive recursive definitions of a function does post-process the result before returning it. The naive definition of a recursive factorial is rather

 proc factorial0 {n} {
    if {$n>1} then {
       return [expr {$n * [factorial0 [expr {$n-1}]]}]
    } else {
       return 1
    }
 }

The idea of the bifurcation command is to provide something similar to recursion by mimicing the fork/join of parallel processes. The body of iterate is split in two parts: one which spawns "recursive" calls and one which joins the results from these spawned calls back up. The factorial implementation there is

 proc factorial3 {x} {
    bifurcation [list $x] n {
       if {$n > 1} then {
          spawn result [expr {$n-1}]
       } else {
          expr 1
       }
    } result {
       expr {$n * $result}
    }
 }

but as the name hints, it is really meant to support recursions which branch out to multiple recursive calls at each step.

[As for the implementation, I think that will have to wait until some other day. It's getting late enough as it is.]


Category Control Structure