[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

revisiting lambda*



[
 Sorry if you receive multiple copies, but I've had some problems
 mailing stuff to rrrs-authors recently.
]

    However, he seems to imply that
    lambda* uses "special values" to pass arbitrary numbers of values.  In fact,
    lambda* uses special VARIABLES to pass arbitrary numbers of values---variables
    which refer to multiple values.  No new sort of data structure or value was
    implied.

There are two different ways to look at it.  You are "blaming" the
variables, rather than the values that they hold.  Ultimately you are
adding a new kind of object, or a new kind of variable, that I (and
many other people) consider unnecessary and distasteful.  I also
suspect that if you try to write the semantics for it, you will find
that it is easier to think about the value being "funny", rather than
the variable.

    Of more general interest is Rozas' claim that lambda* "jumbles" independent
    issues which would be better handled separately.  I found it notable that his
    alternative approach was based on Scheme procedures in all their glory, which
    "jumble" many language features which could be treated separately.  Consider
    some of things achieved by procedures in Scheme:

This argument is very strange.  You are not trying to defend lambda*,
you are instead saying something like
"Since there is already something broken in the language, it is not
surprising that we want something even more broken, and furthermore,
how come you don't argue about the broken things already there?".
Perhaps you are trying to discredit me, rather than my argument?  I
hope not.

I agree that lambda mixes up certain concepts.  Both lambda* and what
I hope to see fix some of these problems.  You are assuming that I'm
just being facetious by arguing against lambda* while you don't hear
me arguing against lambda.  The reason I argue against lambda* is
because it is not yet in the language.  Perhaps if lambda were being
considered for addition, I would be arguing against it too.  That has
not been feasible since 1975.  Another reason why I (and others)
haven't tried to replace lambda is that we don't have anything better,
although perhaps John Lamping's thesis points in the right direction.

After examining your list of capabilities for lambda/procedures, I
detect a basic confusion in your argument (or your interpretation of
my argument):

You must distinguish between the capabilities of procedures, and the
capabilities of the primitive forms (and procedures) that create
procedures.  You don't do that.

I don't object to procedures being able to do everything.  Scheme is
an applicative language, and I don't want to change that.  I do object
to making each individual primitive form that creates procedures be
able to do everything.  I don't want LAMBDA to become Scheme's kitchen
sink in the same way that LOOP is CommonLisp's kitchen sink (or one of
its many kitchen sinks).

Let's examine your list element by element and see whether your
objections are objections against lambda or against procedures in
general.  Again, I have no objections to procedures being able to do
everything.  I have objections against individual special forms (be it
lambda or lambda*) being able to do everything.

   * Passing parameters.

This has nothing to do with lambda, or ultimately even with
procedures.  Procedure applications (ie. combinations) pass parameters.
Procedures RECEIVE parameters.  More on this below.

   * Returning results.

This has nothing to do with either lambda or procedures.  Expressions
"return" (or have) values.  Therefore lambda expressions have values,
combinations have values, and procedure bodies have values.
Perhaps you are confusing the concept of procedures or lambda versus
their implementation with standard (procedure based) compiler technology.

   * Binding and assignment of variables.

Lambda definitely binds variables, but I hardly see how it assigns
variables.  Assignment is handled by set!  Procedures don't bind
anything.  They allocate storage for variables, and potentially mutate
the storage of preallocated variables.

   * Scoping of variables.

Definitely, but this is really the same as the point about binding,
since when you have the ability to bind, you must specify how
conflicts are resolved.  You are not saying anything new.

   * Capture of free variables (lexical scoping and indefinite extent).

Again, this is the same point.

   * Freezing of action (procedures are a form of "potential energy").

Yes.  But this is a property of procedures, not (inherently) of lambda.

   * Passing varying numbers of parameters.

I hope you mean receiving.  Otherwise you are talking about
combinations or APPLY.  If you are talking about receiving, your point
is ambiguous:

- Do you mean that procedures can accept more than one argument, ie. are
you talking about currying?

I certainly don't advocate for a curried language.  I don't see any
advantage (pragmatic or aesthetic) to that.

- Do you mean that a given procedure created by lambda can accept an
unbounded number of arguments, ie. dotted parameter lists?

This point is mentioned below.  At any rate, individual procedures
should certainly be able to receive any number of arguments, but it
doesn't follow that the same primitive must be used to construct all
of these procedures.

   * Access to primitive data structures (the magic of cons, car, etc).

This has nothing to do with lambda.

   * Access to continuations (call/cc).

Again, this has nothing to do with lambda.  As an aside, I think that
the fact that "primitive" continuations are invokable is a mistake,
but it is to late to fight this battle.

   * Converting lists to parameters (apply).

This has nothing to do with lambda.  I still think that it is funny,
and that is what prompted my messages.  Perhaps you've forgotten that.

   * Converting parameters to lists (dot interface).

Both you and I are trying to fix this.  Don't accuse me of defending
it.

    Yet it is certainly this "lambda jumble" that makes Scheme what it is.  I think
    few Schemers would want to lose any of at least the first 6 of the above
    properties of Scheme procedures.  The others are more debatable, but are
    certainly useful in practice.

As I stated before, you are confusing the power of procedures with the
capabilities of lambda.  The power of procedures is what makes Scheme
what it is, not lambda per se!

    Although lambda* adds to the jumble, I think it is the best means to date for
    handling procedures which accept variable numbers of arguments.  Perhaps
    allowing procedures with variable numbers of arguments was our first mistake,
    but we must make the best of it, which was the primary goal of lambda*.  The
    further issue of lambda* and multiple return values is orthogonal, but once
    multiple return values are added to a language it becomes necessary to
    integrate them smoothly with existing features.  lambda* provides solid base
    for adding multiple return values, but in no way depends upon their existence.	

I think that lambda* was valuable in pointing out a different way of
handling dispatch on number of arguments received.  There are problems
with the lambda* model, just as there are problems with the #!optional
(or &optional) model.  I think that the rest argument feature is much
more questionable (and most people agreed at Snowbird), and
furthermore, I believe strongly that multiple values should be handled
by an independent mechanism.  I'm not saying that lambda* is a
"waste".  I'm just saying that it is no panacea, and I would like to
isolate what I consider to be its good features from what I consider
to be its bad features.

    Finally, I think there is little justification for "proceduralizing"
    everything.  I for one am glad that I can write (if test then else) instead
    of (ef test (lambda () then) (lambda () else)).  Sometimes procedures are an
    appropriate way to introduce language features.  cons and call/c are examples
    of such procedures.  But not always.  I like if and I don't think procedures
    like SUPPLY-ARGUMENTS and MAKE-ARGUMENTS-COLLECTOR are an improvement to the
    dot interface.  (I realize Rozas was not submitting them for serious
    consideration, but they are the only examples of what a "procedural" solution
    might look like.)

I hope you mean (test (lambda () then) (lambda () else)).  Besides
isn't this what IF is ultimately? :-)

This is clearly a matter of aesthetics, and yours and mine differ (not
surprising given our different backgrounds).  My PERSONAL preference
is for new capabilities to be added in the form of procedures, not in
the form of special forms.  Assuming that we ultimately agree on
macros (as impossible as that may seem right now), everyone can build
their own cutsey syntax on top of the "raw capability".  I don't
advocate for anyone to necessarily use the primitive capabilities
directly, but I would like them to capture the essence of the concept,
and then people can abstract them away in any way they want.

The reason why I believe that capabilities should be added as
procedures, rather than syntax, is that I view syntax as a shorthand,
whose only purpose is legibility within context of the individual
program I am dealing with.  Since the legibility needs of individual
programs and programmers vary, I don't like to impose any particular
shorthand or syntactic model on anyone.  I believe that the
capabilities should be available by other means, and individual
programmers should map into them in whichever way they find
convenient at any point in time.