Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP
Path: utzoo!mnetor!uunet!seismo!rutgers.rutgers.edu!mit-eddie!uw-beaver!tektronix!reed!bart
From: bart@reed.UUCP (Bart Massey)
Newsgroups: comp.lang.lisp
Subject: Re: EQ and EQUAL (Warning: *TOO LONG* :-) (Re: Importance of REPLACA, REPLACD, and the like?)
Message-ID: <6638@reed.UUCP>
Date: Sun, 19-Jul-87 16:06:44 EDT
Article-I.D.: reed.6638
Posted: Sun Jul 19 16:06:44 1987
Date-Received: Tue, 21-Jul-87 00:44:23 EDT
References: <22@citcom.UUCP> <13278@topaz.rutgers.edu> <6601@reed.UUCP> <3927@spool.WISC.EDU>
Reply-To: bart@reed.UUCP (Bart Massey)
Organization: Reed College, Portland OR
Lines: 119
Keywords: too long

In article <3927@spool.WISC.EDU> neves@ai.WISC.EDU (David M. Neves) writes:
> In article <6601@reed.UUCP> bart@reed.UUCP (Bart Massey, me) writes:
> >At Reed college we are currently doing almost all of our lisp
> >programming in an in-house dialect which supports neither destructive
> >list functions nor dotted pairs.
> ...
> >In case anyone is interested, the following major changes in the
> >language were also implemented, and have proven useful:
> >
> >- Elimination of eq: Using a properly written version of equal, eq is
> >the first thing to happen anyhow, and we've never encountered a
> >situation where knowing that two lists were non-eq is "good enough".

I've gotten several replies already to this statement, so I thought
I'd try to give a little more background and detail about what I meant...

There are three explicit Reed Lisp design goals that are relevant to
this question.  These are:

-- Lisp should be easy for non-lisp-programmers, and non-programmers,
to learn and to read.

-- The internal operation of a lisp interpreter/compiler should, as
much as possible, be hidden from the user, for the convenience of the
implementor as well as the user himself.

-- Efficiency is unimportant up to a constant.  I'm not sure how to
put this clearly...  If the language is designed in such a way as to
force certain algorithms to be higher "order" or to take more than an
"order of magnitude" longer to run, the design should be reconsidered.
If, on the other hand, a language design change would allow algorithms
to run a "small constant" amount faster, it should not be implemented
unless there are other compelling reasons for it.  This still isn't
very well said, but I think you can appreciate what I mean...

> I can construct such a situation.  Imagine a list of lists.  You want
> to find out if a list you have a pointer to is contained within that
> list (i.e. MEMBER).  You want to use EQ as the comparison function for
> efficiency -- especially if the first few characters of the non eq
> lists are the same as the one you are looking for.
> 
> e.g.
> look for (a b c) object in
> List:  ( (a b z) (a b a) (a b c d) (a b q) (a b r) (a b c) )

Yep, this is the classic case.  Let's look at a little different one,
just for the sake of clarity:

	look for (a b c) in target
	( (a b c d) (a b c) (a b c d) (a b c) )

OK, here's what I'm thinking when I see this:

Do I know that both instances of (a b c) in target are the same
pointer?  If not, is this a problem? (In other words, do I really want
to look for (a b c) in target, or for a *specific* (a b c)?)  There
are two cases:

	I)  I want *any* (a b c).  This is by *far* the most common
case in my (admittedly somewhat limited) experience.  In this case, I
can still use eq, but I have several problems:

		I must be very careful that I don't insert (a b c)
into target in any way except by inserting the appropriate pointer.

		Anyone trying to read or debug my code must also be very
careful that I have done so.  This is generally non-trivial.

		I must understand the internals of my implementation
*very well* to insure to myself that I am always putting the proper
pointer in.  Such details actually vary a great deal from
implementation to implementation, so I must write for a specific
interpreter.


	Case II)  I am only interested in finding my pointer.  I don't
really care that it happens to be pointing to the list (a b c).  This
is the case which I have never seen occur in practice, and have a hard
time thinking of an example of.  Cases that *look* like this
generally look that way because the programmer has spent time
trying to figure out where his (a b c)s are.

With respect to the three design goals stated above, I suspect it is
now clear why we didn't implement eq.  It makes lisp harder to
understand, the code harder to read, is implementation dependent.
Finally, note what kind of efficiency increase you get from its use in
the above example.  

In compiled lisp, you get effectively a factor of 5, during this one
line of code, because you are doing 5 comparisons (the list and four
of its elements) in the equal version, to each single test (the list)
in the eq version.  But note that these 5 comparisons are
approximately 20 machine instructions (5 compares, 5 cdrs, and a
(machine-level) recursion), versus the single machine instruction of
eq.  The overhead of executing compiled lisp code effectively swamps
this gain in all but the largest of cases, and thus you're not likely
to be able to measure the speedup in a piece of real code.

	The interpreted case is even worse.  Ours is a fast lisp
interpreter -- approximately 2-10 times as fast as Franz.  In our
lisp, an equal on even a 1000 element deeply recursive list with all
but the last element succeeding is completely swamped by the overhead
of the eval associated with executing the equal, in spite of the fact
that much of our speed advantage is due to a very fast eval.

	I hope this clarifies why we left eq out of our lisp
interpreter.  My friends and I are awfully fond of the concept of
"fast enough", and "The Law Of Virtual Efficiency: A "Real Programmer"
will spend a week to save the user 10 seconds over the life of the
program."

But then again, what do we know???  Precious little!  Please send
replies to me, I will summarize.  BTW, thanks much for all the
flattering and interested responses I've received so far!  This was
the first net article I'd posted in a long time that I didn't receive
*a single flame* about!!  Some useful criticisms, but no flames! How
very pleasant!...

					Bart the Long-Winded :-)