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

Re: Arg count checking



[Removed Bug-1100@SUMEX-AIM]

    Date: 1 Apr 85 01:00 PST
    From: masinter.pa@XEROX.ARPA
    Subject: Re: Arg count checking
    In-reply-to: various messages
    To: bug-1100@SUMEX-AIM.ARPA
    cc: common-lisp@SU-AI.ARPA

    The only places I've seen extra args used in situations where it isn't
    (clearly) a bug is in code that is dealing with two different
    implementations.

    Implementation A has a function FOO with args A B and an optional C.

    Implementation B has a function FOO with args A B. No optional.

    You can then write a client that will work with both implementations, as
    long as it knows that some implementations may ignore the last argument.
    (Most of the cases I've seen have been ones where its been a "flag",
    e.g. implementation A has a "slow" and a "fast" case, and you can say
    (FOO 1 2 T) and it means do (FOO 1 2) and, if you care, do it fast
    rather than slow..., although there have been other uses.)

    I can think of about a half-dozen cases of this, off hand; e.g., the
    "standard system" has a vanilla WHEREIS, but there's a super-WHEREIS
    that takes lots of args, and it redefines WHEREIS to have lots of
    optionals.  

    The odd thing is that I can't think of a really clean way of doing
    this-- having variant argument lists in different implementations -- I
    suppose you'd have to write a &REST in every implementation that didn't
    have the args.

The proper solution would have been for the person who wrote the 
super-WHEREIS to call it something else (or in a language like CL,
use the same name but put it in a different package). Then old code still
calls the old utility and new code can call the new super-WHEREIS.
Even if the super-WHEREIS fixes some misfeature the old WHEREIS wants,
the writer should have done
 (DEFUN WHEREIS (...compatible-args...) (SUPER-WHEREIS ...whatever...))
 (DEFUN SUPER-WHEREIS (...hairy-args...) ...)
so that people wouldn't be tempted to call the existing WHEREIS with
the hairy args and get into the situation you're talking about.

It's the fault of the person who created the new FOO abstraction 
without giving it a new name that you are in this mess.

If (FOO x y) and (FOO x y z) does already exist in two implementations,
you should just write your own GENERALIZED-FOO abstraction that does
 (DEFUN GFOO (X Y &REST Z)
   (COND ((MEMQ 'HAIRY-FOO *FEATURES*) (APPLY #'FOO X Y Z))
	 (T (FOO X Y))))
or some macroesque equivalent (if you're an efficiency fiend).

    Another place this arises are in mapping functions which take functional
    arguments; they apply the functional arg; sometimes they pass extra
    arguments which of course the functional is free to ignore. 

This is interesting. I hadn't ever thought about
 (MAPCAR #'(LAMBDA (X &OPTIONAL (Y 0)) (LIST X Y)) '(A B C D) '(1 2))
I suppose one could argue that returning ((A 1) (B 2) (C 0) (D 0)) would
have been the "right" thing. Of course, 
 (MAPCAR #'(LAMBDA (&OPTIONAL (X 0) (Y 0)) (LIST X Y)) '(A B C D) '(1 2))
might have been a little harder to deal with ... since presumably that
just wouldn't return... of course, the fact that 
 (MAPCAR #'(LAMBDA (&OPTIONAL (X 0)) (PRINT X)))
loops indefinitely rather than returning argues in favor of such a position.
... I think.

    All of these are instances where the CALLER knows the args are optional.

    I can imagine (not very seriously) a separate syntax for that, e.g., put
    the &OPTIONAL on the caller rather than the callee:

    (FOO 1 2 &OPTIONAL T) which says to call FOO with 2 arguments, and throw
    away the third if FOO only takes two arguments, but otherwise pass it
    all three.

This is certainly consistent with the symmetry provided in the allow-other-keys
portion of the keyword arg specs. 

Somehow this bothers me; I'm at a loss to say exactly why, though. But I
think it also bothers me that :ALLOW-OTHER-KEYS is there (for reasons beyond
the obvious fact that it's an ugly special case in the middle of the keyword
namespace), so don't take it too personally. I think it has something to do 
with the fact that it means you're really trying to shoehorn an additional 
abstraction in where room wasn't provided. ie, the real problem is that all 
the old code is still calling FOO instead of GENERIC-FOO. I guess I think 
that's just a bug in that code. 

    Can this be different than multiple-value-return where the caller is
    expecting fewer values than the callee returns?  

I definitely agree these two issues are not different. I think this just
shows that what multiple value return does is wrong. I've held the position
for quite some time that multiple values should have to be &OPTIONAL'd 
and &REST'd. I believe there are others on this committee who side with me,
but we were in the minority in the discussions leading up to the current spec.
Perhaps that will change with time.