Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

lambda abstraction in Tcl?

1 view
Skip to first unread message

Jean-Guy Schneider

unread,
Sep 23, 1998, 3:00:00 AM9/23/98
to
In the context of analyzing the extensibility of Tcl, I run into an problem which
I have not been able to solve so far (maybe it is easy to solve...).

Functional programming languages (and others) have a so-called "lambda" abstraction
which allows to dynamically create an anonnymnous function. This is often used when
a function has to be applied to a collection of entities, and this function is passed
as a parameter to another function (e.g., sorting the elements of a list according to
a comparision function).

Unfortunately, such a lambda abstraction is not present in Tcl, but I assume it should
be possible to build on. I have something like the following in mind:

lambda {args} body

would return a command which takes {args} as its arguments and executes body when
it is invoked. An example

proc apply {func list} {
foreach val $args {lappend res [$fun $val]}
return $res
}

apply [lambda x {return expr "($x + 2) * $x"}] {1 2 3 4 5}
--> {3 8 15 24 35}

There are other areas where such a lambda abstraction would be helpful.

Any hints for a solution are welcome.

Jean-Guy

______________________________________________________________

Jean-Guy Schneider Software Composition Group (SCG)
E-mail: schn...@iam.unibe.ch
WWW: http://www.iam.unibe.ch/~schneidr
Tel: +41 31 631.48.68 --- IAM: 86.81 --- Fax: 39.65
Mail: Institut fuer Informatik (IAM), Universitaet Bern
Neubrueckstrasse 10, CH-3012 BERN, SWITZERLAND
______________________________________________________________

Nat Pryce

unread,
Sep 23, 1998, 3:00:00 AM9/23/98
to
Here's a basic implementation:

proc lambda {_vars _script args} {
if {[llength $_vars] == [llength $args]} {
foreach _v [uplevel 1 {info locals}] { upvar 1 $_v $_v }
foreach _v $_vars _a $args { set $_v $_a }
return [eval $_script]
} else {
error "wrong number of values passed to lambda function"
}
}


Rather than returning a new command, this must be passed to a proc
as a literal script and eval'd within the proc to be applied to
arguments. This is not actually much of a problem -- most Tcl commands
which are passed as arguments need to be eval'd, and so lambda
functions can be passed as callbacks to Tk widgets and so on.

E.g:

proc do_1_2 {cmd} {
return [eval $cmd 1 2]
}

do_1_2 {lambda {x y} {expr {$x + $y}}}
do_1_2 {lambda {x y} {expr {$x * $y}}}
do_1_2 {lambda {x y} {puts "$x $y"}}

If you are used to lazy functional languages, be warned that this
implementation does not create a closure over local variables and
can not be curried.

Cheers,
Nat.
--
+------------------------------------------+---------------------+
| Name: Nat Pryce MEng ACGI | Dept. of Computing, |
| Email: n...@doc.ic.ac.uk | Imperial College, |
| Tel: +44 (0)171 594 8394 | 180 Queen's Gate, |
| Fax: +44 (0)171 581 8024 | London SW7 2BZ, |
| WWW: http://www-dse.doc.ic.ac.uk/~np2 | United Kingdom |
+------------------------------------------+---------------------+

Alexandre Ferrieux

unread,
Sep 23, 1998, 3:00:00 AM9/23/98
to
Jean-Guy Schneider wrote:
>
> Functional programming languages (and others) have a so-called "lambda" abstraction
> which allows to dynamically create an anonnymnous function.

There is no built-in support for lambdas per se, but the accepted idiom
of "arguments appended to script" (frequent in Tk) approximates it,
though to a lesser degree of generality. However, the ease of
introspection of Tcl leads me to believe that a proper combination of
[concat] [list] and [uplevel] could do the trick. A beginning could be:

proc lambda {x s} {return [list lambdaeval $x $s]}

proc lambdaeval {x s val} {
# set $x (I insist on the $) in the proper frame (uplevel) and eval $s
# sorry - no time to delve into that :)
}

proc apply {func list} {
foreach val $args {lappend res [eval $fun $val]}
return $res
}

-Alex

Bryan Oakley

unread,
Sep 23, 1998, 3:00:00 AM9/23/98
to
Jean-Guy Schneider wrote:
>
> In the context of analyzing the extensibility of Tcl, I run into an problem which
> I have not been able to solve so far (maybe it is easy to solve...).
>
> Functional programming languages (and others) have a so-called "lambda" abstraction
> which allows to dynamically create an anonnymnous function. This is often used when
> a function has to be applied to a collection of entities, and this function is passed
> as a parameter to another function (e.g., sorting the elements of a list according to
> a comparision function).
>
> Unfortunately, such a lambda abstraction is not present in Tcl, but I assume it should
> be possible to build on. I have something like the following in mind:
>
> lambda {args} body
>
> would return a command which takes {args} as its arguments and executes body when
> it is invoked. An example
>
> proc apply {func list} {

> foreach val $args {lappend res [$fun $val]}
> return $res
> }
>
> apply [lambda x {return expr "($x + 2) * $x"}] {1 2 3 4 5}
> --> {3 8 15 24 35}
>
> There are other areas where such a lambda abstraction would be helpful.
>
> Any hints for a solution are welcome.

As is almost always the case with tcl, this function can be easily
written. The only caveat is that the created functions aren't completely
anonymous -- commands such as "info commands" would notice them. But
that's a small matter, I suppose.

Here's one quick hack:

proc lambda {arglist body} {
set prefix "__lambda__"
set count 0
while {[llength [info commands "$prefix$count"]] != 0} {
incr count
}

proc "$prefix$count" $arglist $body

return "$prefix$count"
}

proc apply {func arglist} {
foreach val $arglist {
lappend res [$func $val]
}
return $res
}


Here's the output, using your example:

% apply [lambda x {return [expr {($x + 2) * $x}]}] {1 2 3 4 5}


3 8 15 24 35

%

Does that meet your needs?

--
Bryan Oakley
ChannelPoint, Inc.

Jonathan Cook

unread,
Sep 23, 1998, 3:00:00 AM9/23/98
to
Jean-Guy Schneider (schn...@iam.unibe.ch) wrote:
: be possible to build on. I have something like the following in mind:
:
: lambda {args} body
:
: it is invoked. An example

:
: proc apply {func list} {
: foreach val $args {lappend res [$fun $val]}
: return $res
: }
:
: apply [lambda x {return expr "($x + 2) * $x"}] {1 2 3 4 5}
: --> {3 8 15 24 35}
:

The most simple approach (I haven't used namespaces yet):

% set anon.counter 0
0
% proc lambda {Args Body} {
global anon.counter
incr anon.counter
proc anon.${anon.counter} $Args $Body
return anon.${anon.counter}
}
% proc apply {func list} {
foreach val $list {lappend res [$func $val]}
return $res
}


% apply [lambda x {return [expr "($x + 2) * $x"]}] {1 2 3 4 5}
3 8 15 24 35

Of course, this leaves all those anonymous procs around -- but I
don't see any straightforward way of knowing when to clean them up.
It just isn't built into the language (though you probably could
do it conservatively by looking at [info level] -- mmm, probably
not because of global vars...I don't know...you could do it
explicitly...an "unlambda $func" command?)

JonCook.

Eric BOUDAILLIER

unread,
Sep 24, 1998, 3:00:00 AM9/24/98
to
Jean-Guy Schneider wrote:

> Functional programming languages (and others) have a so-called "lambda" abstraction
> which allows to dynamically create an anonnymnous function.
>
>

Here is mine solution, which is a melt of Alex
and Nat Pryce ideas and solution :

# create a lambda function
proc lambda { arglist body } {
return [list lambdaeval {} {} $arglist $body]
}

# call lambda function
# arglist0 / vallist0 -> known arguments
# arglist1 -> missing arguments varname
proc lambdaeval { arglist0 vallist0 arglist1 body args } {
set arglen1 [llength $arglist1]
set vallen1 [llength $args]
if { $vallen1 > $arglen1 } {
return -code error "too many argument"
}
if { $vallen1 < $arglen1 } {
# curried it
return [list lambdaeval \
[concat $arglist0 [lrange $arglist1 0 [expr {$vallen1-1}]]] \
[concat $vallist0 $args] \
[lrange $arglist1 $vallen1 end] \
$body]
}

foreach varname [concat $arglist0 $arglist1] value [concat $vallist0 $args] {
set $varname $value
}
eval $body
}

-example

% set sum [lambda {x y} {return [expr {$x+$y}]}]
lambdaeval {} {} {x y} {return [expr {$x+$y}]}

% eval $sum 2 5
7

% set myincr [eval $sum 1]
lambdaeval x 1 y {return [expr {$x+$y}]}

% eval $myincr 3
4


-eric

lvi...@cas.org

unread,
Sep 24, 1998, 3:00:00 AM9/24/98
to

According to Jean-Guy Schneider <schn...@iam.unibe.ch>:
:Unfortunately, such a lambda abstraction is not present in Tcl, but I

Besides the 4 samples provided so far, note that I see a number of
references to lamda in
<URL:http://www.purl.org/NET/Tcl-FAQ/part5.html>

Also, if others who contributed to this thread want to make your
implementation available via ftp or http drop me a note and I can
add a reference to your URL to the software catalog.

--
<URL:mailto:lvi...@cas.org> Quote: In heaven, there is no panic,
<*> O- <URL:http://www.purl.org/NET/lvirden/> | only planning.
Unless explicitly stated to the contrary, nothing in this posting
should be construed as representing my employer's opinions.

0 new messages