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

Re: disjointness of '() and #f




Arthur Gleckler writes:
>> 
>>    Another factor arguing for disjointness has been raised but unstated for
>>    some time.  Disjointness [(eq? '() #f) -> #f] is much easier to explain
>>    to students as it accords with their intuitive notions.  There are areas
>>    where Scheme differs from naive intuition for deep reasons (e.g.
>>    exactness).  It would be nice to be able to concentrate on areas where
>>    there really are deep issues rather than areas of happenstance.
>> 
>> I don't understand arguments from student intuition, particularly this one.
>> Saying that #f marks ends of lists is just as clear as saying that
>> 
>>  X | (null? X)
>> 
>> marks ends of lists.  Notions like disjointness of types are less intuitive
>> than simple conventions like "#f marks ends of lists."
>> 

Let me take a stab at presenting the problem.  It is very convenient to
have a language-defined object which has a unique type.  The rubric I see
many people (including myself) try to use is:
  "never use #f in a data context, only in a control context".

This is a frequent source of confusion in data structures where 2 pieces
of information is desired: (1) was an action successful? (2) if
successful, what was the value of the result? [multiple value returns is
another discussion].  As (2) is only interesting in the success case, the
attempt is made to overload the returned value for two uses.  E.g.

  (let ( (probe (table-lookup table key)) )
    (if probe
        (success-action probe)
        (failure-action)
  ) )

This works as long as the value of PROBE is #f XOR a valid data value.
There are a couple of ways that people try to `patch' this behavior.  One
way is by packaging the result in a data structure [it then has to be
`unwrapped'].  The other way is to pass success and failure continuations
into the lookup routine [this makes interesting and confusing reading to
novice Scheme programmers as they puzzle out the control flow].  Using a
user-defined unique value [e.g. (define moby-foo (lambda ignore-args
moby-foo)) ] tends to leak through abstractions and uglify the world.

I agree that this problem is one more of education and psychology.
However, it is a fundamental one.  We would have the same problem if we
introduced (eq? #t '{}) as a notation where #\{ and #\} were allowed in
balanced pairs in place of #\( and #\).

I still see bugs in runtime-system code of the form shown above.  My
experience agrees with others who find that "time and again, some subtle
bug, taking hours if not days to find"...

The history is that people have been burned badly enough to state:
"This certainly seems to be yet another opportunity to correct in Scheme
a fundamental mistake in Common Lisp."

The view of many people is that there is a problem here.

>>    I see no problem with making disjointness of predicates BOOLEAN? and
>>    NULL? in the IEEE Scheme Standard and allowing (e.g. Jinx) to redefine
>>    BOOLEAN?, NULL?, LIST?, PAIR?, CONS, et cetera for idiosyncratic
>>    purposes.  As long as such *conventions* can be implemented in a standard
>>    Scheme, there is no creative restriction and many people are saved from
>>    needless confusion. 
>> 
>>    I would be very interested in deep reasons why such confusion should be
>>    mandated.
>> 
>>    Jinx, if you can explain to my simple self why you cannot redefine a
>>    scheme implementation which follows the standard to do what you want in
>>    this case, please do so.  If not, I request that you remove your
>>    objections.
>> 
>> If I assume that () and #f will be eq? in all implementations and write my code
>> accordingly, I'm making a mistake.  I should clearly use null? to detect ends
>> of lists EVEN THOUGH I KNOW THAT MY IMPLEMENTATION MAKES () AND #F EQUIVALENT,
>> but that's no reason that () and #F shouldn't be equivalent in some
>> implementations (for whatever reasons).  After all, if I write
>> 
>>   (+ '(a b c) '(d e))
>> 
>> to append two lists knowing that my implementation of Scheme defines + on both
>> numbers and lists, I shouldn't expect to get the right answer on other
>> implementations.  The language should not have to clean up after careless
>> programmers, after all.  

I typically work in a given week in at least 3 Scheme implementations [on
5 compute platforms under 4 OS's].  Last week I used T, Chez Scheme,
Gambit, ELK, and XScheme.  I can't use I KNOW THAT MY IMPLEMENTATION...
I use other people's code.  I am extremely interested in ultra-portable
bug-free code.  My own experience is that (eq? #f '()) being #t works
against this.

I agree that a language should not have to clean up for careless
programmers.  I should not have to fix `portable' code.  But I do, time
and again.  I would dearly love (if '() 1 2) to always evaluate to 1,
because it would save me much time.

------------------------------------------------------------------
>> To say that Jinx is doing this for "idiosyncratic
>> purposes" is just unfriendly.

I did not mean to imply that of Jinx.  The intent was that *anyone* can
redefine the end-of-list marker in non-standard (~= idiosyncratic) ways
[I have used a function like moby-foo for just this purpose].  This was
assuming that (eq? #f '()) -> #f as a standard [which it currently is
not].  I apologize for the implication.  If I did not think highly of
Jinx, I would not be trying to understand the reasons for his views [I
believe I do understand his position].

I am attempting to `do the right thing as I see it', not be unkind or
unfriendly.  I don't always succeed.  Sorry. (failure-continuation-?)

-Ken