[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
&Rest Lists
Date: Wed, 23 Mar 88 12:50 EDT
From: ELIOT@cs.umass.edu
From: IN%"Moon@SCRC-STONY-BROOK.ARPA" "David A. Moon" 20-MAR-1988 08:07
Subj: &REST args
As I stated in an earlier message I think that Common
Lisp should prohibit sharing of list structure in the last argument
to APPLY.
1. In no other place does Common Lisp automatically unshare structure,
except when the user is explicitly modifying the structure (as in REMOVE).
Making APPLY automatically unshare would be a semantic wart.
Common Lisp does commit itself to doing whatever has to be done so that
interpreted and compiled code behaves the same.
How did the compiler/interpreter distinction enter into this? No one
has proposed distinguishing them. In the environment I use, they both
behave the same regarding &rest lists. And even so, the compiler and
interpreter are allowed to have different ways of dealing with a
situation that is undefined ("is an error", to use CLtL wording),
because portable applications are not permitted to depend on the
behavior in such situations.
Actually I don't care
whether that last argument to APPLY can be shared. But I stongly
believe that Common Lisp should do whatever has to be done so that
all variants of the same function call are equivalent. Consider:
(setq x '(1 2 3))
[1] (apply #'foo 1 2 3 NIL)
[2] (apply #'foo 1 2 (cddr x))
[3] (apply #'foo 1 (cdr x))
[4] (apply #'foo x)
[5] (funcall #'foo 1 2 3)
[6] (eval (cons 'foo x))
[7] (eval (list 'foo 1 2 3))
[8] (foo 1 2 3)
I strongly believe that all of [1-8] are the same function call and
must have the same semantics. Since the list, x, cannot be modified by
[1, 5, 7, 8] it should not be allowed to modify it in [2, 3, 4, 6].
I disagree with this. [5, 7, 8] don't even reference the list x, so how
can they be considered the same? And the difference between [4] and [6]
would be quite obvious if any of the elements of x were objects that
don't self-evaluate, e.g.:
(setq a 1 b 2 c 3 x '(a b c))
(apply #'list x) => (a b c) (which, by the way, should not share
structure with x)
(eval (cons 'list x)) => (1 2 3)
2. If APPLY copies its last argument, recursive programs that receive an
&REST argument and pass it to APPLY become inefficient. A linear time
algorithm can change to a quadratic time algorithm. While the efficiency
could be regained through compiler flow analysis in many cases, I think
it's better not to put the inefficiency into the language in the first
place.
Using APPLY is probably inefficient anyhow.
I sure hope not! Many implementations of EVAL use APPLY internally for
all function calls, something along the lines of:
(apply (symbol-function (car form)) (mapcar #'eval (cdr form)))
(yes, this is extremely simplified, I know that the real thing is much
more complex).
If this kind of recursive
function has to run quickly it would probably be better to define an
auxillary function with a fixed number of arguments to do the real work.
Besides, most function calls are not made using APPLY. Recursive
function calls using APPLY are too rare to justify a blemish in the
semantics of all function calls.
It's not just recursive calls. Consider all the functions that take
arguments just like FORMAT. They all call various intermediate
functions, which call other intermediates, and eventually they call
FORMAT itself. The &rest list shouldn't be duplicated at each call.
Many other functions take &rest arguments that are simply passed on to
other functions using APPLY.
Some might argue that the correct way to disambiguate this situation is
to specify that APPLY must share its final argument with any &Rest list
argument as much as possible. This doesn't make sense to me. I don't
believe that it would make Common Lisp more efficient to any significant
degree. I think it creates an undesirable inconsistency in the
semantics of function calls, as I argued above. Furthermore, modifying
&Rest lists as a way to indirectly modify some argument to APPLY sounds
like one of the worst dirty programming tricks I have heard of in a long
time. The possibility of causing exceedingly obscure bugs makes my skin
crawl.
I don't think anyone is actually suggesting that people intentionally
modify &rest lists. Personally, I prefer making it be undefined whether
&rest lists share, so programmers aren't tempted to write such code.
barmar