Richard Suchenwirth 2002-11-30 - An innocent question in the Tcl chatroom from me, after reading [1], brought DKF to produce the following amazing Tcl code. The background is Combinatory Logic and functional programming, and the discovery of M. Schoenfinkel in 1924 that all functions can be composed from the elementary combinators S and K, which can be written in Tcl as:

proc S {f g x} {$f $x [$g $x]}
proc K {x y} {set x}

However, to reproduce the various derivations, some work was necessary, because function "calls" might have more or less than their defined arguments. At toplevel, Donal creates a function curry that is called like proc, but prepares for cases where the argument count differs from the one specified:}

proc curry {name arglist body} {
# We're going to implement the curried command using a pair of helper procedures
interp alias {} $name {} _curry_impl $name [llength $arglist]
# Create the command-specific helper
proc ${name}_impl $arglist $body ;# the "real thing"
# Arrange for the helper to go away when the front-end vanishes.
trace add command $name delete [list _curry_nuke $name]
}
if 0 {
The generic curry implementation calls the "real thing" if the number of
arguments matches, else does some pretty dense voodoo (means: I at least do
look at it, but don't understand the details),
mostly due to the fact that [eval] runs best with a single pure list:
}

Lars H: Sorry to interrupt, but isn't it simply as follows: the function does not evaluate unless the necessary number of arguments are available (i.e., the command evaluates to itself), whereas if there are are more arguments available than necessary, then the function leaves the extra arguments up ahead as arguments for some other function?

Lars H: The following should make x act like a constant (anything beginning with x expands to itself)

interp alias {} x {} list x

but perhaps a more useful behaviour would be to evaluate everything following the x, like

proc x {args} {linsert [eval $args] 0 x}

According to the claim at the top, though, both of these should be possible to define using only curry, S, and K. I wonder how.

Also, if one is to analyse these things properly (determine how much can be implemented using them) then one must probably be clear about what kinds of grouping are allowed. Can {} and [] behave differently here?

KBK Without answering Lars's questions fully, let's observe that the implementation shown does eager evaluation, where all arguments to a combinator are evaluated prior to evaluating the combinator itself. This method gets us into trouble as we get into more advanced combinator theory.

In particular, I tried to implement the factorial function with the fixed-point combinator Y:

for an appropriate definiton of [zero?]. The eager evaluation made it infinitely recursive. I wasted entirely too much time changing things to do lazy evaluation instead, and wound up with a different Combinator Engine.

DKF - When defining the derived combinators, it's easier if you do it like this: