Version 22 of Accumulator Generators

Updated 2003-04-16 06:54:39

Paul Graham invited solutions to his Accumulator Generator [L1 ] problem written in various programming languages.

Here is the problem:

Write a function foo that takes a number n and returns a function that takes a number i, and returns n incremented by i. Note: (a) that's number, not integer, (b) that's incremented by, not plus.

After a slew of languages are presented that solve the problem, he concludes with:

Some languages are not represented here, either because you can't write this program in them (short of Greenspun's Tenth Rule) or because no one has yet sent me the code for that language. If you know how to write this program in a language that isn't listed here, please send it along. (Please make sure, first, that it actually does the same thing as these solutions.)

(Greenspun's Tenth Rule: any sufficiently complicated C or Fortran program contains an ad hoc informally-specified bug-ridden slow implementation of half of Common Lisp.)

Now, since Tcl is meant to be extended (like Lisp, you extend the langauge to solve problems), I don't think that Greenspun's rule applies. Now, that being said, Tcl doesn't support closures natively, but you can add that capability (sans any GC) to the language quite naturally (see Closures). To keep things short, I offered a solution that didn't use any generalized closure proc, but solved the problem directly:

 namespace eval foo {variable instance 1}
 proc foo {n} {
    set id [namespace eval foo {incr instance}]
    namespace eval foo::${id} [list variable n $n]
    proc foo::${id}::foo {i} { 
        variable n
        set n [expr {$n+$i}]
    }
    return foo::${id}::foo
 }

I haven't heard from him since... (and he did accept my lua submission). I am sure the above code snippet is not the optimal solution. Anyone care to improve upon it (or offer a better solution), post it here and then submit it to Graham?

-- Todd Coram


Talk

RS prefers pseudo-closure with default arguments as it appears less heavyweight (no namespace, no garbage collection needed..) The following variation from procs as objects is as long as the above code, but somehow has a "musical", rhythmic quality:

 proc foo n {
   set name [clock clicks] ;# get a (hopefully) unique name
    proc $name "i {n $n}" {
        set self [lindex [info level 0] 0]
        set n [expr {$n+$i}]
        proc $self "i {n $n}"  [info body $self]
        set n
   }
   set name
 }

See also procs as objects which goes deeper into the topic.


Arjen Markus I will not supply a solution using aliases (yet a third way to achieve the effect), but rather I would like to comment on the term function: it seems to me that these functions with side-effects are not functions in the proper sense of the word. They are more akin to subroutines in Fortran (and they have the same bad habits as C's not-so-functional functions). Proper functions should return a value and that value should be the same whenever the arguments are the same.

I mean this comment as more than just a word game!


Depends on whether you view closures as side-effects. The accumulator variable n is really local to the function (it is enclosed---or captured---by it), so while the function is not functionally pure, it has no global side-effects (n is private to the generated function). -- Todd Coram


I don't understand the "increment, not plus" requirement. Is he asking that, for example, 2.5 + 3 be calculated as (10 * 2.5 + 10 * 3) / 10? I don't get it.

-- What is meant is that the local variable gets updated each time the function gets called. So, the returned function works more or less in the same way as rnd() - each time a different value - AM


I finally figured out why Tcl was not accepted (quote from http://paulgraham.com/accgensub.html ):

I currently believe that you can't write this program in ML and Ocaml (no polymorphism), Tcl (the functions returned by foo have to be named, and there is no sure way to avoid name clashes), or K (doesn't support closures).

Hmmm... I believe my original submission is not subject to name clashes (at least up to the maximum integer that can be held by id). But, enough of this game...If I really wanted an accumulator generator in Tcl, I'd code it in C, Lisp, Java, Perl (or whatever) and wrap it! Never underestimate the power of glue. -- Todd Coram


If you believe your suggestion is valid, why not ask for a clarification from Paul? It's even possible to check if a procedure exists, before creating it... Todd Coram - I had a small email thread with him. There was a problem with my first submission (it only worked with integers) and I sent a corrected version (the one above). I think he may have been put off by the fact that Tcl didn't have anonymous procedures (but I am only guessing since I never heard from him again).

The article is a bit silly, though, if you think about it. It's using the one area Lisp is strong in. F.ex. in Tcl mostly we use code-as-data in the form of callback scripts, not anonymous procedures. We could pick an example such as "write extensions that logs all input/output of any script run through it", which would probably be equally unfair to many other languages.

Having said that, anonymous procedures would probably be a useful addition to Tcl if there was some way to do it properly.

--Setok


TV It seems we want to need lambda after all, and not bother about memory management and variable assignment, stacks and heaps, memory pages, bug free linked lists and memory allocation. When is a function a function ? - RS quotes [L2 ]: "Let A and B be sets. A function f from A to B is an assignment of exactly one element of B to each element of A."


Why so weird way to solve problems? Lets do it objectoriented in XOTcl.

 Class Accumulator -parameter {startValue 0}
 Accumulator instproc addValue {value} {my incr startValue $value}

and using it

 set myAccumulator [Accumulator new -startValue 20]
 puts [$myAccumulator addValue 30]

you can also use strong named objects

 Accumulator create myAccu -startValue 20
 puts [myAccu addValue 30]

One difference. We have objects no functions but this way to solve problems is more than 30 years proofen and it works.