A long time ago, on 9 May, Alan Watson wrote:

> Thanks very much for that explanation. I've got two further comments.
> 
> First, a sufficiently clever compiler could arrange that (lambda (j) (h j))
> have the same tag as h and thereby, with an eqv? that checks tags rather
> than just addresses, preserve R5RS eqv?.

Maybe I'm not sufficiently clever, but I don't see how that could work.
If h is the name of a known local procedure that's referenced only in
call position, then the compiler shouldn't have to allocate a closure
*or* a tag for it.  (Allocating unique tags is almost as expensive as
allocating a closure.)

> Second, the R6RS semantics would be more convincing if they were
> justified on the basis of real code rather than artificial examples.
> I understand that's difficult. I also understand that you're never
> going to convince everyone; I have long given up worrying about
> factors of two.

When I was working with the R6RS editors, we scoured several code
bases looking for real examples that relied upon the R5RS semantics.
We found very few examples.  If I recall correctly, the majority of
the examples we did find were incorrect or at least non-portable.
Almost all of the correct examples we found were efficiency hacks
that were intended to speed up some common special case; since the
general-case code would suffice, these efficiency hacks would be of
no interest to people who don't worry about factors of two.

Consider, for example, the reference implementation for SRFI 69
(Basic hash tables).  Its implementation of make-hash-table calls
appropriate-hash-function-for, which uses eq? (not eqv?) tests on
comparison procedures to try to identify an appropriate hash function.
That attempt succeeds only if the comparison procedure is eq?,
string=?, or string-ci=?.  Otherwise it defaults to a general
purpose hash function that would work just as well with the three
special cases (*).  Hence the only purpose of those tests is to
improve performance.

    (*)  Well, almost as well.  The general purpose hash function
    refuses to hash procedures.  That's just a deliberate bug in
    the reference implementation of the default hash function.

On the other hand, the R5RS semantics for eqv? on procedures
appears to have encouraged the designer of SRFI 69 to make a
serious mistake in his design.  If you provide a comparison
procedure but no matching hash function, the implementation is
supposed to infer an appropriate hash function.  Although that
works for a few simple comparison procedures such as eq? and
equal?, it can't work in general:  There is absolutely no way
to infer an appropriate hash function for comparison procedures
such as

    (lambda (x y)
      ;; the cdrs of x and y consist entirely of irrelevant comments,
      ;; which we should ignore when testing for equality
      (= (car x) (car y))

Bottom line:

1.  The R5RS semantics for eqv? on procedures is hard to use correctly.
Most of the uses we found were incorrect or at least non-portable.

2.  Most of the correct uses that we found were mere efficiency
hacks.  Since the efficiency that's lost by supporting the R5RS
semantics is considerably more important than the efficiency that's
gained, the efficiency argument goes against the R5RS semantics.

3.  The R5RS semantics appears to encourage the design of fragile
APIs that may work for some common cases but don't work in general.

Will

_______________________________________________
r6rs-discuss mailing list
r6rs-discuss@lists.r6rs.org
http://lists.r6rs.org/cgi-bin/mailman/listinfo/r6rs-discuss

Reply via email to