Re: A different design for closures (was: What does Tcl lack?)

From: antirez (antirez_at_remove.gmail.remove.com)
Date: 02/23/05


Date: Wed, 23 Feb 2005 08:47:46 GMT

Bruce Stephens wrote:
> sigzero@gmail.com writes:
>
> [...]
>
>
>>So what exactly does Tcl lack? I am not talking "OO" stuff, because
>>I can take or leave that. Fundamentally in the Tcl language itself
>>is more of what I am asking about.
>
>
> Closures, mostly. And if it had closures, then lambda. And
> presumably proper lexical scoping would then make sense. That's not
> in 8.5, and I'd guess the changes that seem to me to be necessary
> would be too radical for closures ever to get into Tcl, so I'm not
> holding my breath for them to appear in 9.

 From time to time there is this discussion about adding closures to
Tcl, but usually the idea is to make they similar to languages like
Scheme, i.e. closures with lexical scoping. My opinion is that this is
very hard to do with Tcl, and even not in the spirit of the language.
In a Tcl program, what part of a procedure body is a variable is not
defined until execution, for example:

set a 10
closure {x} {
    incr a
}

Then I define 'incr' to be like 'puts', and 'a' is no longer a variable.
It was already suggested that in Tcl closures should not have any kind
of creation-time resolution rule, but that the context where they are
defined should be captured as a whole, and used when the closure is
running to resolve "unbound symbols" (that are better referred as
variables and procedures not otherwise defined during execution in
Tcl slang). Still there is the problem that to similuate the lexical
scoping you have to take *references* to the shared environment, so that:

set a 10
set foo [closure {x} {
    incr x
}]
set bar [closure {x} {
    incr x
}]
$foo ;# -> 11
$bar ;# -> 12

foo and bar will share the same 'a'.

I think this is a all to complex and UnTclish, so I tried to design a
new semantic for closures.

The semantic is based on a single command [closure], that can set/get
closure variables in the scope of the current procedure, together with
a minimal change in the [proc] and [lambda] command (I know we don't
have lambda... but still there is a way Tcl users already think about
it and should change).

Bisically, to set a closure variable, there is to write the following
command:

closure set x 10

this will set 'x' inside the closure of the current procedure. This 'x'
will be persistent accross different calls of this procedure, and all
this environment will be destroied once the procedure itself is
destroied. The procedure can test for the existence of a closure
variable with [closure exists x], can get the value with [closure get x]
and so on.

The change required to the [proc] command, is that it can take an
optional further argument, that's a Tcl list of key/value pairs used
to inizialize the closure at procedure creation time.

so:

proc foo {} { .... } {x 10 y 20}

will create the procedure foo with x=10 and y=20 inside the closure.
The same for lambda.

The following is an example of procedure that returns a progressive
integer number every time it's called:

proc counter {} {
    if {![closure exists x]} {
         set x 0
    }
    set x [closure get x]
    closure set x [expr $x+1]
    return $x
}

Of course it's better to use the optional argument
of proc and write it as:

proc counter {} {
    set x [closure get x]
    closure set x [expr $x+1]
    return $x
} {x 0}

Of course for this to be very useful, lambda it's needed.
This version of lambda, like proc, should accept the optional
argument to initialize the closure. This is an example:

proc createAdder {x} {
    lamba {y} {expr $y+[closure get $x]} [list x $x]
}

set f [createAdder 5]
$f 10 ;# -> 15

I think that this design can do everything lexical scoping is able to
do, but with a command-based interface that plays better with Tcl.

This kind of closures will be added into the Jim interpreter, a
small footprint Tcl interpreter I'm writing. This interpreter is already
working and I'll be happy to send a preview tar.gz to interested
people (the license is the APACHE2, so it's possible to use Jim in
commercial projects if needed).

Regards,
Salvatore



Relevant Pages

  • Re: closures and dynamic binding
    ... The double lambda is conceptually more sound in some ... I agree that the default argument syntax is an abuse, ... don't think there's a way to create a closure in Python without ... overrides the outer scope. ...
    (comp.lang.python)
  • Re: Lisp in hardware
    ... > pre-computed index into an environment. ... You have to "pre-compute" a LAMBDA just as much (or rather, ... because the closure object has to hold the captured lexical ...
    (comp.lang.lisp)
  • Re: Cost of closures
    ... a closure consists of a piece of code and a set of lexical ... by the same lambda while the environment part is different. ... in a register or on the stack. ... it must indirect through the `this' pointer. ...
    (comp.lang.lisp)
  • Re: I thought this was the one that worked?
    ... It's still a closure. ... closure for a block regardless. ... That lambda still has a reference to ...
    (comp.lang.ruby)
  • Re: I thought this was the one that worked?
    ... I guess you could debate whether that block is a closure, ... you've turned it into a Proc, so technically the Proc, rather than the ... not calling a Proc object derived from the ... It comes down to the fact that blocks are syntactic constructs, ...
    (comp.lang.ruby)