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


    Date: Thu, 5 Feb 1987  14:06 EST
    From: "Scott E. Fahlman" <Fahlman@C.CS.CMU.EDU>

    It's good design philosophy for an implementor not to exploit loopholes
    in the spec in ways that will screw users.  As I said before, in this
    particular case, any user who gets in trouble was skating too close to
    the edge anyway, but I still don't think you should set traps for the

That's a strange idea of "good design philosophy".  It seems to me that
we are trying to write a specification for a portable common subset
here.  It seems to me that the specification should say one of two

[1] All implementations of Common Lisp are REQUIRED to obey the
following contract.  Any implementation that does not follow the
contract is in VIOLATION of the definition.  Any program written in
Common Lisp is FREE to depend on and take advantage of the contract.

[2] The following aspect of the operation of this function is NOT
REQUIRED.  Implementations might choose to have this aspect, or not have
this aspect, or work a different way, because this aspect is NOT part of
the contract.  Any program written in Common Lisp is FORBIDDEN to depend
on this aspect, or else it is not portable.

You seem to feel there is a third class:

[3] The following aspect of the operation of this function, while NOT
REQUIRED by the Common Lisp spec, because it doesn't say anything about
the aspect one way or another, is REQUIRED anyway because Fahlman deems
it to be a "loophole".  Any program in Common Lisp is FREE to depend on
it, except that they might not run in implementations where nobody was
quite sure which things were considered "loopholes" or not.

In other words, either we should SPECIFY the behavior of REMF and
friends EXPLICITLY as part of the Common Lisp definition, or we should
admit that it is UNSPECIFIED, and implementations can do what they want,
and users had better be aware of it.  I don't see how there can be any
middle ground.

In other words, we should be quite explicit about what's required and
what's not required, and make sure it's stated clearly in the spec.

Here, it seems to me that we are discussing a tradeoff.  We can (1) make
the behavior more specifically defined in terms of the nitty-gritty that
it does; Pro: users can depend on that nitty-gritty and still have their
programs be portable; Con: implementations are restricted to work that
way, even if it degrades performance; or (2) make the behavior be
defined more abstractly; Pro: the system has more latitude to implement
things more efficiently; Con: the users can only use the function for
its abstract purpose, and not take advantage of the way it works inside.

Either we decide that the users are too damned close to the ice, they
should not be doing those things, and (2) is the way to go, or we decide
that this is a trap for the unwary, users have to or want to depend on
these internals, and (1) is the way to go.  I don't see how we can have
it both ways.