guile-devel
[Top][All Lists]
Advanced

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

guile and elisp


From: Andy Wingo
Subject: guile and elisp
Date: Thu, 25 Mar 2010 13:22:25 +0100
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/23.0.92 (gnu/linux)

Hi all,

For 2.0 I have a very short list of things to do, mostly documentation
and compatibility work, but there is at least one feature: finishing
Mark Weaver's integration of nil into Guile.

If you're just catching up, Elisp has "nil", which is both false and the
end-of-list. Nil works fine in Elisp, but we would like to tightly
integrate Scheme and Elisp, allowing procedures and data to be passed
back and forth -- so we need a way for Scheme to see nil as false.

So! Here's a table of how various functions respond to various inputs.
To make the table more readable, a result of #t is written as T, and a
result of #f as F, and nil as NIL.


                 #t    #f   '()  nil   otherwise
Scheme:
  boolean?       T     T    F    T     F
  not            F     T    F    T     F
  null?          F     F    T    T     F
Elisp:
  booleanp       T     T    T    T     NIL
  not (elisp)    NIL   T    T    T     NIL


I guess I was assuming from the get-go that elisp's T would be Scheme's
#t; one could decide otherwise, but that would be pain for no gain. So
already we see how practical considerations are creeping in :)

Currently in Guile, things are as the table describes; though it could
be that the elisp functions return F instead of NIL. But the whole point
here is to allow Elisp to see #f as nil, so I'm not particularly worried
about this. (I'll be writing test cases according to these tables).

One can see however that introducing a value to which e.g. BOOLEAN? or
NOT respond differently than the "otherwise" case can break code that
assumes that, for example, `(not foo)'` implies that `foo' is #f
(whereas in the future it can be nil also). We don't see these cases
now, because we aren't mixing Scheme and elisp much. But I would argue
that that code is broken; that it should test for /properties/ via the
NULL?, NOT, etc instead of testing for values.


More tricky is what should happen with equality predicates, and the
fallout thereafter. We could make any decision here; but here's one set
of decisions:

     EQ?  #t   #f   '()  nil
     #t   T    F    F    F
     #f        T    F    F
     '()            T    F
     nil                 T

This table shows the result of calling `eq?' between these four values,
specifically that #t is only eq? to #t, #f to #f, and so on. The missing
half of the table is redundant.

EQV? could be like EQ? or like EQUAL?; I am inclined to make it like
EQ?. So, same considerations for EQV?.

But what about this one:

  EQUAL?  #t   #f   '()  nil
     #t   T    F    F    F
     #f        T    F    T
     '()            T    T
     nil                 T

If we disregard #t, because it's not interesting, and cut out the
identity cases, we get the following three rules:

   (equal? #f  '()) => #f    ; required by scheme
   (equal? #f  nil) => #t    ; interop
   (equal? nil '()) => #t    ; interop

But then you break the transitive property of equality!

[An aside: This equal? behavior would require `hash' (not `hashq' or
`hashv') to hash #f, nil, and '() to the same value, then to be
distinguished via `equal?' while traversing the bucket. I don't think
`hash' presents more problems than that.]


                             ***

What to do?

In a perfect world, we want '(a b c . #%nil) to be EQUAL? to '(a b c .
()). But as I showed above, we can't do that without breaking some
corner cases.

It's of course an option to just make `(equal? #f nil) => #f'. But this
would limit the interoperability of Scheme and Elisp. I was telling this
to our fringe languages group last night, and Andrew Bagdanov remarked
that it would be amusing if opaque types like buffers were more
interoperable than pairs.

It would also be an option to translate data from Elisp to Scheme and
back, when going across the language barrier, but this pains me on many
levels; it destroys EQ? semantics, and it changes algorithmic space/time
complexities.

Another option would be to present an abstraction above pairs, or
sequences in general, as Clojure has done. (Incidentally, this decision,
combined with support for lazy sequencies, led Hickey to remove the
concept of nil-as-singleton from his language.)


These are the four options that I see. For myself, the interoperability
benefits of considering '() as equal? to nil are quite compelling, and
outweigh the practical implications of the transitive equal? wart. But,
I wanted to mention this analysis and these options so that interested
people can give feedback, and we can document our decision.

(Actually, thinking on it a little more, maybe it would be the right
thing to do to just keep nil not equal? to #f. But it's a complicated
question, and my brain is tired :))

Thoughts?

Happy hacking,

Andy
-- 
http://wingolog.org/




reply via email to

[Prev in Thread] Current Thread [Next in Thread]