Subject: PP'ing interpreted OBJECTs...
From: <[email-masked]>
To: [email-masked]
Date: 19 Nov 85 23:47:19 EST (Tue)

PP does something strange with OBJECTs, as illustrated by the following
transcript.  Am I doing the right thing?  How do I get a non-compiled OBJECT
to PP correctly?

> (define foo (object (lambda () 'value-of-foo)
                      ((identification self) 'foo)
                      ((print self stream) (format stream "#{FOO}"))))
[Defining FOO] #{FOO}
> (pp foo)

** Error: operation not handled
  (COMPILED-CODE-SOURCE ())
>> *** EOF *** 

;; Adding a PRETTY-PRINT method doesn't help.
;; ------------------------------------------

> (define foo (object (lambda () 'value-of-foo)
                      ((identification self) 'foo)
                      ((print self stream) (format stream "#{FOO}"))
                      ((pretty-print self stream) (format stream "#{FOO}"))))
[Redefining FOO] #{FOO}
> (pp foo)

** Error: operation not handled
  (COMPILED-CODE-SOURCE ())

>> (procedure? foo)
T

It seems to me that T thinks FOO is a procedure and so tries to do a
WHERE-DEFINED on it when I try to PP it.  Shouldn't PP first look for
a PRETTY-PRINT method, or at least be smart enough not to look for the
WHERE-DEFINED of a interpreted object?  Or did I do something wrong?

-- Ashwin.

-------

Date:     19 Nov 85 (Tue) 22:50:43 EST
From:     Keiji Kanazawa <[email-masked]>
To:       [email-masked]
Subject:  suspend T

What modifications do I need to make to T2.27 to enable it to suspend
on sr9 (DOMAIN/IX) Apollos? Do I bother?

Date: 20 Nov 85 10:50:28 EST (Wed)
From: <[email-masked]>
Subject: re: PP'ing interpreted OBJECTs...
To: [email-masked]
Cc: [email-masked]

That looks like a bug to me, but there is something you can do if you are willing
to sacrifice some of the functionality of OBJECTs.  The problem seems to be coming
from the fact that T thinks objects that are callable (ie that have a procedure   
associated with them) are procedures, not objects.  If you make your objects
uncallable, they pretty-print, so:

> (define foo1 (object ()
                       ((pretty-print self str) (format str "#{Foo1}"))))
#{Object 159}
> (pp foo1)

#{Foo1}
> 

;; Notice that uncallable objects print as #{Object x} and that callable ones print 
;; as #{Procedure y}

> (define foo2 (object (lambda () 'value-of-foo)
                       ((pretty-print self str) (format str "#{Foo2}"))))
#{Procedure 158}
> (pp baz1)

** Error: operation not handled
  (COMPILED-CODE-SOURCE ())
>> *** EOF *** 

Unfortunately, operations also have the same pretty-printing problem.  *sigh*

                                                    Larry
-------

Date: 20 Nov 85 12:39:08 EST (Wed)
From: <[email-masked]>
Subject: Re: #T, self-evaluation, and NULL-LIST =? FALSE.
To: jar@mit-mc
Cc: [email-masked]

    In T, #T and #F are external syntax for true and false *objects*, not
    *expressions*.  #T and #F are not themselves supposed to be evaluable
    expressions.  Evaluable expressions which give true and false values are
    T and NIL, or, if you prefer, (TRUE) and (FALSE).

However, note the following behavior:

   > (eq? (true) '#T)
   ()

What then is the purpose of #T (or equivalently the *TRUE-OBJECT*)?  I realize
the difference between #T and (TRUE), but I guess I was trying to say that
I didn't see the need for #T, given that we have (TRUE) (or T, if you prefer).
It just seemed like unnecessary syntax (shudder!) to me.

    In the long run I think the right thing to do is to somehow make the way
    that boolean and other objects evaluate (macroexpand) be a property of
    the syntax table...

Again, why do we need syntax for booleans when we have T, NIL, (TRUE) and (FALSE)?
    
                                      ... (it's bad enough that numbers
    self-evaluate; where does one draw the line?)...

I disagree.  The value of 34 is 34, not ERROR.

                                      ... (It would have helped if we
    had distinguished #F from () back in 1981 when we had a chance.)
    
We all use the fact that the null list and the false object are the same while
programming.  This isn't due to any theoretical confusion (any more than making
use of (CAR '()) => () is); it just happens to be convenient sugar for something
that occurs very often.  The sugar helps in avoiding code clutter which would
otherwise result from type checking all over the place.  In addition, I think
this also helps in defining the semantics of some functions, such as ASS.  Now
we can simply say that ASS returns a pair (which may be NIL if the key isn't
in the A-list) without worrying about whether "not being in the A-list" means
returning a NULL pair or the boolean FALSE.  Of course, the user can always
hand the result of ASS to (COND ((NULL? result) -not-found-) (ELSE -use-pair-)),
but then this gets us back to the code clutter point.

-- Ashwin.

-------

Date: 20 Nov 85 15:32:14 EST (Wed)
From: <[email-masked]>
Subject: Re: #T, self-evaluation, and NULL-LIST =? FALSE.
To: <[email-masked]>
Cc: [email-masked]

        In T, #T and #F are external syntax for true and false *objects*, not
        *expressions*.  #T and #F are not themselves supposed to be evaluable
        expressions.  Evaluable expressions which give true and false values are
        T and NIL, or, if you prefer, (TRUE) and (FALSE).
    
    However, note the following behavior:
    
       > (eq? (true) '#T)
       ()
    
    What then is the purpose of #T (or equivalently the
    *TRUE-OBJECT*)?  I realize the difference between #T and (TRUE),
    but I guess I was trying to say that I didn't see the need for
    #T, given that we have (TRUE) (or T, if you prefer).  It just
    seemed like unnecessary syntax (shudder!) to me.
    
The point is #T cannot be shadowed.  Since TRUE is just an identifier
it is not guaranteed to evaluate to the same thing in all environments.
The manaul says that (TRUE) returns some true object not the cononical
true object.

        In the long run I think the right thing to do is to somehow
        make the way that boolean and other objects evaluate
        (macroexpand) be a property of the syntax table...
    
    Again, why do we need syntax for booleans when we have T, NIL,
    (TRUE) and (FALSE)?
        
No. T, NIL, TRUE, and FALSE are identifiers bound in the standard
environment, and thus they can be shadowed.  #T and #F are read
syntax and are guaranteed to read as the canonical true and false
objects in any environment using the standard read table.

        ... (it's bad enough that numbers self-evaluate; where does
        one draw the line?)...
    
    I disagree.  The value of 34 is 34, not ERROR.
    
No again.  Your confusing the identifier represented by the character
sequence "34" and the number 34.  Maybe Jonathan can expand on this.

        ... (It would have helped if we had distinguished #F from
        () back in 1981 when we had a chance.)
        
    We all use the fact that the null list and the false object
    are the same while programming.  ...  The sugar helps in avoiding
    code clutter which would otherwise result from type checking
    all over the place.

You shouldn't be.  The null list and the false object are not
semantically the same and for example the compiler could detect
certain errors that it now can't if this difference were supported
in the language.  I don't see how making #F different from () causes
code clutter, could you explain?

    - Jim

    

Date: Wed, 20 Nov 85 16:01:48 EST
From: Jonathan A Rees <[email-masked]>
Subject:  #T, self-evaluation, and NULL-LIST =? FALSE.
To: [email-masked]
cc: [email-masked]
In-reply-to: Msg of 20 Nov 85 12:39:08 EST (Wed) from <ram%YALE-RING at YALE.ARPA>
Message-ID: <[MIT-MC.ARPA].725680.851120.JAR>

    Date: 20 Nov 85 12:39:08 EST (Wed)
    From: <ram%YALE-RING at YALE.ARPA>

    However, note the following behavior:

       > (eq? (true) '#T)
       ()

This is consistent with the message I sent out; the value of T is only
guaranteed to be true.  It's not guaranteed to be any particular true
object, and programs shouldn't ever depend on any aspect of its value,
other than its trueness.  It just happens in version 2.8, the variable T
evaluates to the symbol T.  In 2.9 it evaluates to something else.

    What then is the purpose of #T?  I realize the difference between #T
    and (TRUE), but I guess I was trying to say that I didn't see the
    need for #T, given that we have (TRUE) (or T, if you prefer).  It
    just seemed like unnecessary syntax (shudder!) to me.

The purpose of the syntax #T is to give a way to write a true boolean
value as a component of a composite literal datum.  It's basically
documentary: it gives a way to say that some part of a datum should be
true, but it prevents someone reading the code from imagining that
there's something important about the value used other than its
trueness.

E.g.: one might write
   (define (dark? color)
     (cond ((assq color
		  '((brown . #t) (black . #t) (white . #f) (yellow . #f)))
	    => cdr)
	   (else (error "unknown color"))))

If "t" is used instead of "#t", the code works the same, but using "#t"
makes it just a little clearer what's going on.

                                          ... (it's bad enough that numbers
        self-evaluate; where does one draw the line?)...

    I disagree.  The value of 34 is 34, not ERROR.

Disagreement is fine.  I agree that it's convenient to be able to write
'34 instead of 34.  I just wanted to point out that something strange is
going on when things "self-evaluate," since it's not the case e.g. that
(CONS X Y) evaluates to the list (CONS X Y).  Applying Occam's razor
here would dictate that the extra feature of self-evaluation for certain
expressions is unnecessary, so the extra complication should be
eliminated.  Since T has this feature, the design of T is not consistent
with Occam's razor.

                                          ... (It would have helped
        if we had distinguished #F from () back in 1981 when we had a
	chance.)

    We all use the fact that the null list and the false object are the
    same while programming.  This isn't due to any theoretical confusion
    (any more than making use of (CAR '()) => () is); it just happens to
    be convenient sugar for something that occurs very often.  ...

It would also be convenient to define (1 V) to mean the same thing as
(VREF V 1); that would avoid code clutter.  There are some languages in
which nearly all programs are correct.  This makes for very concise
code, and simplifies implementations since no error system or debugging
facilities are needed.

Having the domains boolean and list intersect is as theoretically
confused as (CAR '()) => (), namely very.  You have to go to great
lengths in the documentation, implementation, and formal semantics to
cause these things to happen.  It certainly is well-defined (and you
could therefore say it's not confused), but it's not elegant.

I suppose it's a matter of taste.  When you design a language you decide
what the domains of various primitives should be, and what coercions are
allowable when.  In Snobol4 you can write "2" + "2" and get 4; in
Algol68 you can write "2" + "2" and get "22".  These are two of my
favorite languages, and both have many excellent features which avoid
code clutter.  But these days I prefer disjoint domains, even if
increased code clutter results.  In particular, I think boolean, list,
symbol, and number should all be disjoint domains.  Simplifications like
this make programs easier to understand and debug, and streamlines both
the description and the implementation of the language.

Date: Wed, 20 Nov 85 16:09:13 EST
From: Jonathan A Rees <[email-masked]>
Subject:  suspend T
To: [email-masked]
cc: [email-masked]
In-reply-to: Msg of 19 Nov 85 (Tue) 22:50:43 EST from Keiji Kanazawa <kgk%brown.csnet at CSNET-RELAY.ARPA>
Message-ID: <[MIT-MC.ARPA].725684.851120.JAR>

    Date: 19 Nov 85 (Tue) 22:50:43 EST
    From: Keiji Kanazawa <kgk%brown.csnet at CSNET-RELAY.ARPA>

    What modifications do I need to make to T2.27 to enable it to suspend
    on sr9 (DOMAIN/IX) Apollos? Do I bother?

Sorry, no way.  I think some kind of suspend for Aegis T may be in the
works for a future release.

Date: Wed, 20 Nov 85 16:16:14 EST
From: Jonathan A Rees <[email-masked]>
Subject:  PP'ing interpreted OBJECTs...
To: [email-masked]
cc: [email-masked]
In-reply-to: Msg of 19 Nov 85 23:47:19 EST (Tue) from <ram%YALE-RING at YALE.ARPA>
Message-ID: <[MIT-MC.ARPA].725702.851120.JAR>

    Date: 19 Nov 85 23:47:19 EST (Tue)
    From: <ram%YALE-RING at YALE.ARPA>

    PP does something strange with OBJECTs, as illustrated by the
    following transcript.  Am I doing the right thing?  How do I get a
    non-compiled OBJECT to PP correctly?

    ** Error: operation not handled
      (COMPILED-CODE-SOURCE ())
    >> *** EOF *** 

This is definitely a T bug, and ought to be fixed.

Public service announcement:  Remember, T development is a labor of love
- I may be wrong about this, but I was under the impression that no one
was getting paid to do it.  Bugs like this are pretty easy track down
(in this case, some piece of code is failing to check that for false
before calling COMPILED-CODE-SOURCE, or something like that - use GREP
or BACKTRACE to find the places where it's called, etc.), and the T
sources are readily available, at Yale at least.  Any help from users on
actual bug fixes is appreciated, and leaves time for Jim & David &
Richard to do more interesting things (like making suspend work on
Aegis).

If I remember your particular situation, you probably want to be
handling the DISCLOSE operation anyhow, not PRINT or PRETTY-PRINT.

Date: 21 Nov 85 10:06:04 EST (Thu)
From: <[email-masked]>
Subject: Re: #T, self-evaluation, and NULL-LIST =? FALSE.
To: [email-masked]

    ...  But these days I prefer disjoint domains, even if increased
    code clutter results.  In particular, I think boolean, list, symbol,
    and number should all be disjoint domains.  Simplifications like
    this make programs easier to understand and debug, and streamlines
    both the description and the implementation of the language.

Whereas intersecting domains can lead to programs that are easier to
write (the only item not on your list).  APL hackers often use the fact
that true and false are 1 and 0, respectively.  For example, the APL
1-line prime-finder can count divisors just by summing the result of
testing for zero remainders (summing-- not counting!).  Code like this
is indeed tricky to explain and defend, but it is also very easy to write
and reconstruct.

In the hacking days of my youth, I made use of any overlap in functionality
I could find.  (I even found some nice uses for the fact that UCI Lisp
atoms were really lists with a special header!) These days, when most
of the code I write is for tutorial or systems-type purposes, I too prefer
the purer approach to language design, as exemplified by T. I'd like
to think that I've seen the light, but it may just be a change in what
I have to do.
-------

Subject: Bug in CHAR-NAME:  A clue!
Date: Mon, 18 Nov 85 14:28:45 EST
From: Ashwin Ram <[email-masked]>
To: [email-masked]

CHAR-NAME RASSQ's down an internal table to look up a character's name.  It
turns out that if I reDEFINE RASS in *T-IMPLEMENTATION-ENV* with the exact
code (from TSYS/LIST2.T) that it originally was anyway, CHAR-NAME now works
fine.  Curiouser and curiouser...

Ashwin.

-------

Date: 6 Dec 85 15:13:04 EST (Fri)
From: <[email-masked]>
Subject: REQUIRE and friends
To: [email-masked]

[I realize that REQUIRE isn't released.]

While trying to load T3 into ALFL, I got the following error message:

        ** Error: illegal to create new bindings in this environment
          (ENV-LOOKUP ... %LOOP:EXPAND-MACRO #t #t)

This is apparently because I used REQUIRE within a BIND form - thus 
not at top level.  

Why can't I use REQUIRE at any other than top level???

Perhaps REQUIRE should take an optional environment parameter???

Should I just use *REQUIRE instead?  

Sigh.

                                --- Jonathan

-------

Date: Fri,  6 Dec 85 17:19:11 EST
From: Jonathan A Rees <[email-masked]>
Subject:  REQUIRE and friends
To: [email-masked]
cc: [email-masked]
In-reply-to: Msg of 6 Dec 85 15:13:04 EST (Fri) from <young%YALE-RING at YALE.ARPA>
Message-ID: <[MIT-MC.ARPA].744766.851206.JAR>

    Date: 6 Dec 85 15:13:04 EST (Fri)
    From: <young%YALE-RING at YALE.ARPA>

    [I realize that REQUIRE isn't released.]

    While trying to load T3 into ALFL, ...

Wow!  I want that version of ALFL.

    I got the following error message:

            ** Error: illegal to create new bindings in this environment
              (ENV-LOOKUP ... %LOOP:EXPAND-MACRO #t #t)

    This is apparently because I used REQUIRE within a BIND form - thus 
    not at top level.  

    Why can't I use REQUIRE at any other than top level?

One day I was making some changes to the implementation of environments
and felt like removing various random features like this.  I think I
justified this by saying I didn't want people trying to imagine that T's
DEFINE worked like MIT Scheme's DEFINE, and in fact I wanted to leave
open the option of changing this at some point, so I added some error
checks to *DEFINE as a first step towards liberation.  I think there
were other considerations, but I can't recall now.  This particular
error can't possibly happen unless somehow you have your hands on one of
T's "immutable environments," which you can't get unless you're running
in a debugger B breakpoint or you're using undocumented things like
REQUIRE and THE-ENVIRONMENT.

    Perhaps REQUIRE should take an optional environment parameter?

REQUIRE isn't released because I never convinced myself it was a
reasonable thing to have.  It was inspired by the USING/ENVIRON feature
of Cambridge Algol68, which has the semantics (although not the
implementation) of textual inclusion.  REQUIRE also happened to have the
additional feature of demand loading of a named modules (this is what
Common Lisp's REQUIRE was, later).  Actually these are two (or three)
different features which should have been implemented orthogonally;
*REQUIRE sort of does the load-on-demand part.  But I never got around
to working on it.  Part of it was that I could never figure out a good
operating-system- and site-independent way to name modules.  You would
like to write modules which use and demand-load each other, but not have
to change your source code when going from one machine to another.  You
would like each program to potentially have its own space of module
names, to avoid collisions.  (Common Lisp really lost on this count; the
space of module (and package) names is flat.)  *REQUIRE and LOAD
maintain an environment-specific list of loaded modules, but I don't
think that's quite the right thing.

So eventually only the lower-level LOAD got put into the manual, and
rather than document REQUIRE or *REQUIRE, we just waited hoping that
once locales really worked, a good proposal for a module system would
fall into our laps.  It never did.  John Lamping worked on one during
summer of '84, but than I stopped doing T development, and I don't think
anyone else has picked up on it.

    Should I just use *REQUIRE instead?  

Do whatever you want.  *REQUIRE probably won't go away until there's a
better way to deal with demand loading.

Why don't you design & propose a module system for T?  At the very least
we could argue about the problem better if there was something solid to
critique.  All the basic mechanism is there: environment creation and
access, loading files into mutable environments, tables, and so on, so
there's plenty of opportunity for experimentation.  You clearly don't
want LOOP to be loaded into the same environment that your program is
running in; but then somehow the system has to decide what environment
LOOP should be loaded into, how to let it provide things for export, how
to get the things it exports, and so on.  There's a modest amount of
engineering required.  T's mutable environments are very general, and
it's just a matter of deciding on a discipline for their use.  Some
protocol must be adopted as standard if we're going to have an
environment where users can share software with each other.

From: <vis!greg@SDCSVAX>
To: [email-masked]
Subject: Any T for Sun workstations?
Date: 10 Dec 85 21:46:04 PST (Tue)

Does anyone know of an implementation of T for a Sun workstation?
Is anyone working on such an implementation?  Does anyone want to
work on such a thing?

I'm willing to help if an implementation is in progress.

Thanks for any info you have,

_Greg Davidson			Virtual Infinity Systems, San Diego

[email-masked]			ucbvax--| telesoft--|
[email-masked]		decvax--+--sdcsvax--+--vis
[email-masked]		 ihnp4--|  noscvax--|

Date: 13 Dec 1985 23:14:24-EST
From: [email-masked]
To: [email-masked]
Subject: T on a Sun

I believe some of the guys at the Centre Mondial Informatique et 
Resource Humaine in Paris were somewhat interested. There's a lot
of interest in Scheme in France, it seems.
	-Olin

To: [email-masked]
From: Jim Meehan <[email-masked]>
Date:  Mon, 23 Dec 85 16:45:06 EST
Subject: Interaction between syntax-tables and environments

There's been a lot of Common Lisp mail recently about the optional
"environment" argument to MACROEXPAND and related functions, although
environments are not very well explained in the Common Lisp manual.  Of
course, I think T's notion of syntax tables is much cleaner, and I was
about to suggest that if they're going to make some changes, they might
do well to consider something like syntax tables.  But one nagging problem
we've faced in T is that there *is* an interaction between
macro-expanders and the environment, and it can cause trouble.  Short,
grotesque example:

(DEFINE-SYNTAX (KAR X) `(CAR ,X))

(LET ((CAR CDR)) (KAR '(1 2 3))) => (2 3)

The person who defined KAR probably wanted the expanded code to use the
standard CAR, but because macros manipulate S-expressions and not values,
there's nothing to prevent the environment from thwarting his intentions.
Of course, you could write

(DEFINE-SYNTAX (KAR X)
  `((*VALUE *STANDARD-ENV* 'CAR) ,X))

but then you lose in efficiency, open-compilability, etc.  One way around
this is to extend the rules for evaluation to permit the CAR of a form
to be a procedure (as opposed to a symbol that is bound to a procedure).
(A similar thing is currently implemented for special forms.)  If this
were implemented, we could write

(DEFINE-SYNTAX (KAR X) `(,CAR ,X))

or, being very careful,

(DEFINE-SYNTAX (KAR X)
  `(,(*VALUE *STANDARD-ENV* 'CAR) ,X))

and avoid the problem.  This introduces other problems (e.g., external
representation, compiler), but it might be workable.

Comments?  Other suggestions?

To: Jonathan A Rees <[email-masked]>
From: Jim Meehan <[email-masked]>
Date:  Sun,  8 Dec 85 00:59:16 EST
Subject: Re: REQUIRE and friends
Cc: [email-masked]

    ... So eventually only the lower-level LOAD got put into the manual, and
    rather than document REQUIRE or *REQUIRE, we just waited hoping that
    once locales really worked, a good proposal for a module system would
    fall into our laps.  It never did.  John Lamping worked on one during
    summer of '84, but than I stopped doing T development, and I don't think
    anyone else has picked up on it.
    
Bill Ferguson and I designed and implemented a "module system" last year,
and while it certainly doesn't solve all the world's problems, we're
happy with the way it turned out.  It has proved quite useful in preventing
interference among various cooperating "subsystems" (e.g., graphics,
Common Lisp interface, cross-compiler) that need separate read-tables,
syntax-tables, and environments.  In the process, we got rid of
STREAM-READ-TABLE and ENV-SYNTAX-TABLE, and we introduced "workspaces"
(triples consisting of a read-table, a syntax-table, and an environment).
Workspaces are the coin of the realm for EVAL, LOAD, REQUIRE, HERALD,
COMFILE, and various other sorts of transducers.  I'd be happy to send
the documentation to anyone interested.

Date: Mon, 23 Dec 85 18:02:42 EST
From: Jonathan A Rees <[email-masked]>
Subject:  Interaction between syntax-tables and environments
To: csi!meehan@UUCP
Cc: [email-masked]

    Date: Mon, 23 Dec 85 16:45:06 EST
    From: Jim Meehan <csi!meehan%UUCP at YALE.ARPA>

    (DEFINE-SYNTAX (KAR X) `(CAR ,X))

    (LET ((CAR CDR)) (KAR '(1 2 3))) => (2 3)

    The person who defined KAR probably wanted the expanded code to use the
    standard CAR, but because macros manipulate S-expressions and not values,
    there's nothing to prevent the environment from thwarting his intentions.
    Of course, you could write

    (DEFINE-SYNTAX (KAR X)
      `((*VALUE *STANDARD-ENV* 'CAR) ,X))

    but then you lose in efficiency, open-compilability, etc.  

You don't lose if the compiler is reasonable.  I think this is almost
the right thing.  I think future versions of the compiler will be able
to compile this correctly.

Of course, it just begs the question, since the user could have done
(LET ((*VALUE LIST)) (KAR 3)), or bound *STANDARD-ENV*.  Ultimately I
think a new special form will have to be added to the language (absolute
reference CAN be made to special forms); that's why *VALUE has a * in
its name, just in case we decided to add a VALUE special form.  (MIT
Scheme has such a special form; it's called ACCESS, but takes its
arguments in the opposite order.)

							       One way around
    this is to extend the rules for evaluation to permit the CAR of a form
    to be a procedure (as opposed to a symbol that is bound to a procedure).
    (A similar thing is currently implemented for special forms.)  If this
    were implemented, we could write

    (DEFINE-SYNTAX (KAR X) `(,CAR ,X))

You don't need to extend the syntax of combinations, you just need to extend
the syntax of QUOTE:

(DEFINE-SYNTAX (KAR X) `(',CAR ,X))

    or, being very careful,

    (DEFINE-SYNTAX (KAR X)
      `(,(*VALUE *STANDARD-ENV* 'CAR) ,X))

There's no reason to do this; the macro expander knows perfectly what
its own environment is (weel... on second thought... but let's not get
into that); there's no problem with ITS binding of CAR.

    and avoid the problem.  This introduces other problems (e.g., external
    representation, compiler), but it might be workable.

This could be made to work for system-supplied procedures, but making
it work for general user procedures would be very difficult; in the
general case the system would have to invoke FTP or the international
telephone or mail systems.

One idea I'm considering is something resembling Internet "domain
naming," which is basically a programming convention which allows one to
name any object on any machine.  If I can work the kinks out and if
someone bothers to implement it, it may become part of some future
release of T, and ultimately T may even be able to invoke FTP
automatically.

Your points are correct; I hope that this further illustrates to people
just how intractible macros are.  Don't use them if you can avoid it!
The above is a terrible example; you could have written

(define-integrable (kar x) (car x))

or

(define-integrable kar car)

without sacrificing a nanosecond of efficiency (this is one of the great
features of T compilers).  There are occasional cases where macros are
desirable (I generally only use them for top-level forms), but this is
not one.

Date: Sun, 29 Dec 85 21:02:27 EST
From: Jonathan A Rees <[email-masked]>
Subject:  [csi!meehan%UUCP: syntax]
To: [email-masked]
Message-ID: <[MC.LCS.MIT.EDU].768311.851229.JAR>

Date: Mon, 23 Dec 85 22:52:38 EST
From: Jim Meehan <csi!meehan%UUCP at YALE.ARPA>
To:   Jonathan A Rees <rees at YALE.ARPA>
cc:   csi!ferguson at UUCP
Re:   syntax
Message-Id: <6603@csi-ring>
In-Reply-To: Your message of Mon, 23 Dec 85 18:02:42 EST

[Jonathan: If you think this would be interesting or appropriate for
the T-DISCUSSION bboard, feel free to forward it there.]

=========================================================================
         ...
         (DEFINE-SYNTAX (KAR X) `(CAR ,X))
         (LET ((CAR CDR)) (KAR '(1 2 3))) => (2 3)
         ...
    The above is a terrible example; you could have written
    (define-integrable (kar x) (car x))
    
Right.  The example I should have used was this:

(LET ((CAR CDR)
      (L '(1 2 3)))
  (POP L))

=========================================================================
    ...
    I hope that this further illustrates to people just how intractible
    macros are.  Don't use them if you can avoid it! ...  There are
    occasional cases where macros are desirable (I generally only use
    them for top-level forms)...

I'm puzzled.  At the moment, in T 2.8, there are 13 special forms and
71 macros in the *STANDARD-SYNTAX-TABLE*.  I'd put the 71 macros into
3 categories:

I. Macros useful primarily at top level:
DEFINE-CONSTANT                   DEFINE-PREDICATE            
DEFINE-STRUCTURE-TYPE             DEFINE-SYNTAX               
DEFINE-INTEGRABLE                 DEFINE-SETTABLE-OPERATION   
DEFINE (also inside LET)          DEFINE-OPERATION
DEFINE-METHODS 

II. Macros useful (i.e., reasonably meaningful) anywhere:
DESTRUCTURE                       WITH-OUTPUT-TO-STRING    
DESTRUCTURE*                      PUSH                     
CASE                              XCOND                    
WITH-OUTPUT-WIDTH-STREAM          XSELECT                  
BLOCK0                            WITH-INPUT-FROM-STRING   
MODIFY                            DECREMENT                
MACRO-EXPANDER                    OR                       
LET*                              SELECT                   
COND                              DELAY                    
WITH-OPEN-STREAMS                 INCREMENT                
SET                               ITERATE                  
WITH-OUTPUT-TO-LIST               EXCHANGE                 
MODIFY-LOCATION                   BIND*                    
POP                               XCASE                    
LET*                              DO                       
AND                               SWAP                     
UNWIND-PROTECT

III. Macros that might be [reasonably implemented as] special forms,
but are useful anywhere, not just at top level:
OPERATION                         OBJECT 
LOCATIVE                          LOCALE 
LET                               LABELS 
CATCH                             BIND   

IV. Other macros (mostly implementation stuff):
DEFINE-INSTANCE-METHODS           %DEFINE-SYNTAX               
IGNORE                            DEFINE-MACRO                 
DECLARE-SETTER                    REFERENCE                    
^                                 DEFINE-STRUCTURE-SELECTOR    
COMMENT                           IGNORABLE                    
OPERATION-DISPATCH                UNTRACE                      
**BACKQUOTE-MARKER**              GC-DEFER                     
HANDLER                           IMPORT                       
SYNONYM                           IF-INTEGRATED                
REQUIRE                           HERALD                       
TRACE                             PP                           

While there's lots of room to argue about whether certain macros (e.g.,
the ones in Categories I and III) could or should be special forms, I
don't know how you can avoid all 32 of the macros in Category II, and
I doubt you'd recommend making them all special forms.

Date: Sun, 29 Dec 85 21:34:52 EST
From: Jonathan A Rees <[email-masked]>
Subject:  syntax
To: [email-masked]
cc: [email-masked]
In-reply-to: Msg of Mon 23 Dec 85 22:52:38 EST from Jim Meehan <csi!meehan%UUCP at YALE.ARPA>
Message-ID: <[MC.LCS.MIT.EDU].768328.851229.JAR>

    Date: Mon, 23 Dec 85 22:52:38 EST
    From: Jim Meehan <csi!meehan%UUCP at YALE.ARPA>

    While there's lots of room to argue about whether certain macros (e.g.,
    the ones in Categories I and III) could or should be special forms, I
    don't know how you can avoid all 32 of the macros in Category II, and
    I doubt you'd recommend making them all special forms.

This is a good point; however, there's a very big difference between
language-provided syntactic extensions like AND, OR, and LET, and the
fact that user-supplied macros are desirable.  Clearly the language
should have a tiny set of primitive special forms (5 is about the right
number, although I think a T kernel would end up having about 11), and
syntactic sugar should be clearly marked as such in any reasonable
manual.

I'll go over your catalog anyhow, to see whether the existence of any of
T's macros would argue for the desirability of user-written macros.  I
don't think they do.

    II. Macros useful (i.e., reasonably meaningful) anywhere:
    COND 	XCOND
    CASE	XCASE
    SELECT	XSELECT
    OR
    DELAY
    SET
    ITERATE
    DO
    AND

I use these, but practically never want to invent new things like them,
so I wouldn't consider it a terrible inconvenience to have the set of
such things (mostly pretty fundamental control constructs) bound at
language design time.

    MACRO-EXPANDER

This is a very peculiar one which I don't understand quite what to do
with.  It's probably completely unnecessary.

    DESTRUCTURE
    DESTRUCTURE*
    BLOCK0
    DECREMENT
    LET*
    INCREMENT                
    BIND*                    
    POP
    PUSH

I rarely use any of the above, and given my druthers would probably
prefer to see them disappear from the manual & implementation.

    WITH-OUTPUT-WIDTH-STREAM
    WITH-INPUT-FROM-STRING
    WITH-OPEN-STREAMS
    WITH-OUTPUT-TO-LIST
    UNWIND-PROTECT
    WITH-OUTPUT-TO-STRING    

These have no right to be macros.  They should be procedures which take
procedure arguments, just as in MIT Scheme.

    MODIFY
    MODIFY-LOCATION
    EXCHANGE                 
    SWAP                     

Locations have second-class status in T, and if things like this are to
exist at all, locations should be made first-class.

    III. Macros that might be [reasonably implemented as] special forms,
    but are useful anywhere, not just at top level:
    OPERATION                         OBJECT 
    LOCATIVE                          LOCALE 
    LET                               LABELS 
    CATCH                             BIND   

I don't quite understand the distinction between II and III, but again I
don't see quite how users would want to extend this set.  CATCH and BIND
should be procedurized; OBJECT should be primitive; all the others are
trivial syntactic sugar, but deal with semantically primitive concepts,
not special members of an open-ended class of features.

    IV. Other macros (mostly implementation stuff):
    DEFINE-INSTANCE-METHODS           %DEFINE-SYNTAX               
    IGNORE                            DEFINE-MACRO                 
    DECLARE-SETTER                    REFERENCE                    
    ^                                 DEFINE-STRUCTURE-SELECTOR    
    COMMENT                           IGNORABLE                    
    OPERATION-DISPATCH                UNTRACE                      
    **BACKQUOTE-MARKER**              GC-DEFER                     
    HANDLER                           IMPORT                       
    SYNONYM                           IF-INTEGRATED                
    REQUIRE                           HERALD                       
    TRACE                             PP                           

Many of these (like IF-INTEGRATED) are disappearing, and exist only due
to design or implementation inadequacies; others are part of the user
interface, not the language (like PP and TRACE), and should not be
implemented by expressions; others are either primitive or trivial.
Again, I don't see how I, or users generally, would want to write macros
similar or analogous to these.  If they do it probably implies a gross
deficiency in the language, and has a better solution than the
sledgehammer of a user macro facility.

I'm not saying there shouldn't be a macro facility, or that the language
shouldn't have a limited amount of syntactic sugar, but rather that I
practically never see a legitimate use of a macro by anyone other than a
language designer.  It's good to be able to be a language designer, but
it's a subtle and difficult art, and correct, orthogonal implementation
is even harder than correct, orthogonal design.

If people have good counterexamples to this argument, I would like to
see them.

Jonathan

Date: Sun 29 Dec 85 20:49:24-PST
From: Andy Freeman <[email-masked]>
Subject: Re: syntax
To: [email-masked]
cc: [email-masked], [email-masked]

Jonathan Rees (JAR@MIT-MC) writes:
    ... I practically never see a legitimate use of a macro by anyone
    other than a language designer.

I haven't decided how to cover macros in my Common Lisp class, but I
recently abused macros.  I'd like to hear about other tools that would
have worked as well.

I've been studying coroutine and generator implementation techniques.
As part of this, I implemented Icon's control structures several ways.
My source programs are READable (as in, they can be read using READ),
unlike real Icon programs, but I'm lazy.  I didn't want to write code
to read them.  (I did write code to translate from my pseudo-Icon
syntax to real Icon syntax so that my test programs could be run by my
implementation as well as Arizona's real Icon implementation.)

I defined two macros to support this project.  The first one defines
procedures; its first argument is the procedure name, the second is
the argument list, and the subsequent arguments are the body of the
definition.  The macro translated this form into a block containing
several side effects to store the various translations I made.  (At
one time, I generated five different versions.)  Compiling a source
file containing procedure definitions saved the work of creating these
translations.  (Initially, these translations were very expensive,
often taking several cpu minutes for a small test program.)

The second macro I used did whatever was necessary to execute one of
the translation.  Some of the translations are native T code, so in
these cases, the macro expands to code that includes all of the
previously defined procedures.  In this case, compiling a file
(containing the macro) compiles the T code that implements the
program.  This is very important; it would be necessary even if I did
write code to read Icon programs and translate them.  (Yes, I could
write a T source file from the translations and then compile it, but
....)

One of the versions I generated used upwards continuations.  T doesn't
support them, but chez scheme does.  Therefore, I wanted my system to
run in both chez scheme and T.  The common subset of these two
languages doesn't allow procedure or macro definition, so I used
macros to define a larger common subset.  (Other differences were
filled in with procedures.)  The decision to use macros instead of
program to read and translate the pseudo-Icon programs helped here as
well; I didn't have to write common subset I/O code.

This isn't the first time I (or others) have pulled this common
language trick.  Once I convinced Interlisp and Maclisp to use the
same source.  Naturally this involved lots of hoops and whistles
(mapcar in the common dialect was called @mapcar), but it was workable
and used for a complete version of MRS.

These may not count as "good counterexamples", but macros made my work
possible.  <insert chain-saw analogy>

-andy
-------

Date: Mon, 30 Dec 85 15:48:40 EST
From: Jonathan A Rees <[email-masked]>
Subject:  syntax
To: [email-masked]
cc: [email-masked], [email-masked]
In-reply-to: Msg of Sun 29 Dec 85 20:49:24-PST from Andy Freeman <ANDY at SU-SUSHI.ARPA>
Message-ID: <[MC.LCS.MIT.EDU].768944.851230.JAR>

Your message doesn't contradict my quoted claim:

    ... I practically never see a legitimate use of a macro by anyone
    other than a language designer.

You WERE being a language designer/implementor - you were designing an
embedded implementation of Icon.

I also use macros when I'm playing with new languages, or trying to run
code in multiple Lisp implementations.  The main reason I write macros
instead of file transducers is so that the embedded language fits in
neatly with all the user interface tools developed for use with the
native Lisp: LOAD, the read-eval-print loop, editor interfaces, and
compilers.

I guess I don't see this technique as a case where one extends the
syntax of the underlying language; really a new language is being
invented.  It would be nice to have multi-lingual user interfaces, so
that one needn't write macros and pollute the underlying environment just
in order to make it as easy to use the new language as it is to use the
old.  By not writing a file transducer, you weren't just being lazy; you
were being modular.  I'd like to have a better programming interface to
the user interface than the macro chainsaw.

- Jonathan.

From: <[email-masked]>
Date: 30 Dec 85 15:43:08 EST (Mon)
Subject: Re: Interaction between syntax-tables and environments
To: Jim Meehan <csi!meehan@UUCP>
Cc: [email-masked]

                                                  ...  But one nagging problem
    we've faced in T is that there *is* an interaction between
    macro-expanders and the environment, and it can cause trouble.

Of course there is.  Macros, once expanded, call procedures and use other
macros.  Since these are gotten to through symbols, there must be an environment
in which these symbols are evaluated.  Now you gotta decide whether these
work as in closures, or you want to look up the values at run-time.  (The former,
I hope, at least for procedures;  possible also for macros (see below).)    

    (DEFINE-SYNTAX (KAR X) `(CAR ,X))
    
    (LET ((CAR CDR)) (KAR '(1 2 3))) => (2 3)
    
    The person who defined KAR probably wanted the expanded code to use the
    standard CAR...

This is the most common case, since we love non-dynamic binding and use it
(and expect it) all the time.  Why should this work for procedures but not
for macros?

    Comments?  Other suggestions?

I'm not sure how easy it is to separate syntax tables and environments (and,
for that matter, read tables).  I liked your "workspaces" idea.  I think of
syntaxes as being associated with the environments in which they are defined,
in fact almost as if the "value" of a symbol was a syntax, analogous to the
treatment of procedures, and syntax tables were an implementational detail.
The advantage of this is uniformity with the closure idea.

All but the most trivial macros refer to other macros (or special forms) and
to other procedures too, just like procedures do.  And just like procedures
are closed at definition time and run in the environment in which they
were defined, I would like my macros to have the symbols they use refer to
the value they have in the definition environment.  One way to ensure this
is to use things like

   (*value *standard-env* 'CADR)    ;(or a special form version of *VALUE)

for standard procedures, just like you use

   (t-syntax 'BLOCK)  =  (syntax-table-entry *standard-syntax-table* 'BLOCK)

for standard macros in T2.9 and T3.  But it gets painful to wrap a reference
around every symbol in a macro definition.  Just like you can have:

   (define (kar x) (car x))

which you can import into another environment and have it work correctly even
if CAR is defined differently there, I would like to be able to do the same with:

   (define-syntax (kar x) `(car ,x))

For the same reason, I think making the user wrap (T-SYNTAX ...) around syntax
references is a copout; if KAR refers to a macro, it should be taken from the
definition environment of KAR.

Syntaxes seem ugly in T because they are too much like dynamic binding.  Handling
them like procedures should make them a nicer genre of beast to work with.

-- Ashwin.

-------

From: <[email-masked]>
Date: 1 Jan 86 13:13:48 EST (Wed)
Subject: macros
To: [email-masked]
Cc: [email-masked], [email-masked]

    Jonathan:
    I'm not saying there shouldn't be a macro facility, or that the
    language shouldn't have a limited amount of syntactic sugar, but
    rather that I practically never see a legitimate use of a macro
    by anyone other than a language designer.

Of course, this begs an answer to the question: "What is a language
designer?"  It would be tempting for each of us to argue that the only
time we use macros is when we're playing the role of language designer;
it's a rather subjective concept.

In fact, for a language designer, macros are a rather constrained
form of syntactic extension -- one might ultimately prefer tools that
allow full context-free parsing.  Since such tools don't currently
exist in the convenient framework of a REPL and it's associated
utilities (which is what's so nice about macros), then one has to
resort to source-to-source transformations (compilation!) if macros
don't fill the bill.

    If people have good counterexamples to this argument, I would
    like to see them.

I almost never use macros, because it's almost always the case that
procedural abstraction will do the job just as well (usually better).
But it seems to me that legitimate uses of macros are always tied
to the desire to delay the evaluation of something.  For example,
one of three macros that we use in the ALFL compiler is the following
very *simple* one:

    (define-syntax (smt ob)
        `(let ((done ()))
           (lambda ()
              (if done done (set done ,ob)) ) ) )

SMT stands for "self-modifying-thunk" and is our way of emulating
graph-reduction without the inefficiency of T's DELAY (this works
because () is not a valid value in ALFL).  Do you think this use is
legitimate?  If so, am I being a language designer?  I suppose I could
have used a procedure like:

    (define (smt delayed-ob)
        (let ((done ()))
          (lambda ()
             (if done done (set done (delayed-ob))) ) ) )

but then I would incur the inefficiency of *two* nullary closures
instead of one.

    -Paul

Date: Thu,  2 Jan 86 16:19:33 EST
From: Jonathan A Rees <[email-masked]>
Subject:  macros
To: [email-masked]
cc: [email-masked]
In-reply-to: Msg of 1 Jan 86 13:13:48 EST (Wed) from <hudak%YALE-RING at YALE.ARPA>
Message-ID: <[MC.LCS.MIT.EDU].771100.860102.JAR>

Obviously T's DELAY and FORCE should be made more efficient.

Date: 28 Jan 86 06:22:59 EST (Tue)
From: <[email-masked]>
Subject: Of growing code and diminishing hacks...
To: [email-masked], [email-masked]

    A SHORT BALLAD DEDICATED TO THE GROWTH OF PROGRAMS
    ==================================================
                      by
                  Ashwin Ram
    
    This is a tale of a sorry quest
    To master pure code at the T guru's behest
    I enrolled in a class that appealing did seem
    For it promised to teach fine things like T3 and Scheme
    
    The first day went fine; we learned of cells
    And symbols and lists and functions as well
    Lisp I had mastered and excited was I
    For to master T3 my hackstincts did cry
    
    I sailed through the first week with no problems at all
    And I even said "closure" instead of "function call"
    Then said the master that ready were we
    To start real hacking instead of simple theory
    
    Will you, said he, write me a function please
    That in lists would associate values with keys
    I went home and turned on my trusty Apollo
    And wrote a function whose definition follows:
    
        (cdr (assq key a-list))
    
    A one-liner I thought, fool that I was
    Just two simple calls without a COND clause
    But when I tried this function to run
    CDR didn't think that NIL was much fun
    
    So I tried again like the good King of yore
    And of code I easily generated some more:
    
        (cond ((assq key a-list) => cdr))
    
    It got longer but purer, and it wasn't too bad
    But then COND ran out and that was quite sad
        
    Well, that isn't hard to fix, I was told
    Just write some more code, my son, be bold
    Being young, not even a moment did I pause
    I stifled my instincts and added a clause
    
        (cond ((assq key a-list) => cdr)
              (else nil))
    
    Sometimes this worked and sometimes it broke
    I debugged and prayed and even had a stroke
    Many a guru tried valiantly to help
    But undefined datums their efforts did squelch.
    
    I returneth once more to the great sage of T
    For no way out of the dilemma I could see
    He said it was easy -- more lines must I fill
    with code, for FALSE was no longer NIL.
    
        (let ((val (assq key a-list)))
           (cond (val (cdr val))
                 (else nil)))
    
    You'd think by now I might be nearing the end
    Of my ballad which seems bad things to portend
    You'd think that we could all go home scot-free
    But COND eschewed VAL; it wanted #T
    
    So I went back to the master and appealed once again
    I said, pardon me, but now I'm really insane
    He said, no you're not really going out of your head
    Instead of just VAL, you must use NOT NULL instead
    
        (let ((val (assq key a-list)))
           (cond ((not (null? val)) (cdr val))
                 (else nil)))
    
    My song is over and I'm going home to bed
    With this ineffable feeling that I've been misled
    And just in case my point you have missed
    Somehow I preferred (CDR (ASSQ KEY A-LIST))
    
                        :-)    
    ==================================================

-------

From: <[email-masked]>
Date: 28 Jan 86 23:36 PST
Subject: Re: Of growing code and diminishing hacks...
To: [email-masked]
Cc: [email-masked], [email-masked]

(define (overloaded-cdr val)
	(if (null? val) nil (cdr val)))

(overloaded-cdr (assq key a-list))

	In other words, if you want a cdr that horribly overloads nil, then
write one.  As for me, I think of a cons cell as an object with car and
cdr fields in it, just as a point is an object with x and y fields in
it.  How about:

(define (overloaded-x point)
	(if (null? point) nil (x point)))

From: <[email-masked]>
Date: 29 Jan 86 01:14 PST
Subject: Re: Of growing code and diminishing hacks...
To: [email-masked]
Cc: [email-masked], [email-masked]

	And another thing.  The only reason NIL == #F usually works out so well
in lisps is that the most common domain of concern is lists.  In both C
and APL, FALSE == 0 since a very common domain of concern is numbers.
Hopefully we are moving towards a programming style that is more
abstract datatype or object based, and so the primacy of lists will
diminish.  In any case, I don't want my programming language prejudging
for me what types of object should be my major concern, and compromising
its foundations to optimize for that one type.

Date: Wed, 29 Jan 86 08:58:51 EST
From: "George J. Carrette" <[email-masked]>
Subject:  Overloading of empty list as false.
To: [email-masked]
Cc: [email-masked], [email-masked], [email-masked]

The primacy of lists in lisp is due to the fact that lisp is its own
meta language and the vocabulary of that meta language is constructed
inductively from lists, symbols and other constants.  Furthermore, the
most general graphs can be constuctured entirely in terms of
bifurcations represented as cons cells; and bifurcations have a
primacy over trifurcations etc. Topping this off by using the logical falsity
constant as the token to mark the end of certain kinds of graphs is
entirely natural definition to use.

The lists themeselves will remain in their primacy as a prefered construct of
efficient assembly language programming, (especially if this means your
assembly language is LISP on a LISPMACHINE). Take note Plummer's multilisp
emulator that he implemented on the 3600: Comments in the code indicating that
using (SETQ *STACK* (CONS ELEMENT *STACK*)) was more efficient than
using the C/FORTRAN/PASCAL style INDEX plus ARRAY, because the GC overhead
was smaller than the expense of the extra instructions and memory references
needed to manipulate the more complicated data structure.

But I am arguing the barn door closed after the cows have left.
The recent slant of revised-revised-report-on-scheme has
indeed cleaned up things to make them more acceptable to the
general community; throwing out all the obvious meta language
machinery and with it much of the preference for lists.

The reason that FALSE == 0 in C is not due to the concern for the domain
of numbers. (Since in C one will often mark the end of character strings
with 0, and represent the NULL pointer as 0 also). FALSE == 0 because
of the instruction sets and memory organizations of the machines
on which C was developed. Similarly in PDP10 Maclisp and CADR Lispmachine
NIL == 0 (as a machine location address).

Date: 29 Jan 86 09:39:05 EST (Wed)
From: <[email-masked]>
Subject: Re: Of growing code and diminishing hacks...
To: <[email-masked]>
Cc: [email-masked], [email-masked]

    (define (overloaded-cdr val)
    	(if (null? val) nil (cdr val)))
    
    (overloaded-cdr (assq key a-list))

    	In other words, if you want a cdr that horribly overloads nil, then
    write one.  As for me, I think of a cons cell as an object with car and
    cdr fields in it, just as a point is an object with x and y fields in
    it.  How about:
    
    (define (overloaded-x point)
    	(if (null? point) nil (x point)))

    
So  now  we  have a proliferation of functions.  You could just as well think
of CAR and CDR as operations, and NIL as  an  object  that  handles  them  by
returning  NIL.  Much cleaner.  Well, maybe not; I guess that's debatable.  I
think the code is cleaner even though the semantics that  the  compiler  must
implement  may  become  a  little hairier.  But that's what compilers are for
:-). 

-------

Date: 29 Jan 86 09:49:57 EST (Wed)
From: <[email-masked]>
Subject: Re: Of growing code and diminishing hacks...
To: <[email-masked]>
Cc: [email-masked], [email-masked]

    	And another thing.  The only reason NIL == #F usually works out so well
    in lisps is that the most common domain of concern is lists.  In both C
    and APL, FALSE == 0 since a very common domain of concern is numbers.

I didn't say that NIL was in any way the best choice for FALSE across all
languages.  In Lisps, NIL = #F is just as reasonable as 0 = FALSE in C or
APL.  Try telling a C programmer that 0 won't be FALSE anymore. 
-------

Date: Tue, 18 Feb 86 15:52:19 est
From: [email-masked] (George Young)
To: [email-masked], [email-masked]

I am a new user of t2.9, (hoping for t3) running bsd4.2 on a vax/780,
developing cad software for wafer-scale integration.  Please add me to
the t-discussion mailing list (or whatever appropriate mailing list exists).
	Thanks,
	George R. Young ( [email-masked] )
	MIT Lincoln Laboratory, Lexington, Mass. 01273

Date: 21 Feb 86 17:31:16 EST (Fri)
From: Ashwin Ram <[email-masked]>
Subject: CASE and SELECT
To: [email-masked]
Cc: [email-masked], [email-masked]

BUG:
----
I think there is an inconsistency in the definition of CASE and SELECT in T.
Both should take an extra first argument, which is the predicate to use in
the comparison, and CASEQ and SELECTQ should be defined to use EQ? as the
default predicate.  (This is in keeping with MEM? and MEMQ?, ASS and ASSQ,
etc.)  Similarly, there may also be a CASEV and SELECTV.

FIX:
----
The fixed code for CASE follows.  Feel free to use it if you like.  (I've
been calling it CASEF so as not to clobber the original CASE, but I think
that CASE and CASEQ is a better way to go.)  The code is tested.

;;******************************************************************************
;; CASE with specifiable PREDicate to compare the cases with.
;; Original CASE is equivalent to: (CASE eq? key . clauses)  [now called CASEQ].
;; Ashwin Ram, May 1985.

(define **case-fell-off-end** '**case-fell-off-end**)

(define-syntax (case pred key . clauses)
  (labels (((expand-case-1 keyvar clauses)
            (if (atom? clauses) '**case-fell-off-end**
              (let ((clause (car clauses))
                    (lose (lambda () (syntax-error "bad ~s clause syntax: ~s"
                                                   'case
                                                   (car clauses)))))
                (cond ((atom? clause) (lose))
                      ((list? (car clause))
                       `(if (or ,@(map (lambda (k) `(,pred ,keyvar ',k))
                                       (car clause)))
                            ,(blockify (cdr clause))
                          ,(expand-case-1 keyvar (cdr clauses))))
                      ((memq? (car clause) '(t else)) (blockify (cdr clause)))
                      (t (lose)))))))
    (let ((keyvar '%%%%key%%%%))
      `((lambda (,keyvar)
          ,(expand-case-1 keyvar clauses))
        ,key))))

(define-syntax (xcase pred key . clauses)
  `(case ,pred ,key ,@clauses (else (losing-xcase))))

(define (losing-xcase)
  (error "no clause selected in ~s expression" 'xcase))

(define-syntax (caseq key . clauses)
  `(case eq? ,key ,@clauses))

(define-syntax (casev key . clauses)
  `(case alikev? ,key ,@clauses))

;;******************************************************************************

-------

Date: 26 Feb 86 13:45:38 EST (Wed)
From: Charles Martin <[email-masked]>
Subject: Re: Bug fix for Ashwin's CASE
To: [email-masked]
Cc: [email-masked], [email-masked]

Minor programming style point.  Instead of doing things like

    (define-syntax (case pred key . clauses)
      (let ((comparator '%%%%casefun%%%%))
        ... ))

I've always preferred constructions such as

    (define-syntax (case pred key . clauses)
      (let ((comparator (generate-symbol 'CASE-FUN)))
        ... ))

(Personally, "%%%%CASEFUN%%%%" is one of my favorite temporary
variable names, so my code would break with your version... :-)
-------

Date: 25 Mar 86 14:31:11 EST (Tue)
From: Jonathan Young <[email-masked]>
Subject: Tables
To: [email-masked]

I would like to see some discussion about desirable features for
the T table package.

Recall that the current ("Released", although it isn't in the manual)
table package supports the following operations:

        (MAKE-TABLE tag)        => table
        (TABLE-ENTRY table key) => value or nil if none
                                        (settable)
        (SET-TABLE-ENTRY table key value) => undefined
                                (adds an association)

Now, the wish list.

Clearly, it is desirable to have hash tables where the hash function
and comparision function are other than POINTER-HASH (or some other
hack) and EQ?.  Obvious examples are <STRING-HASH, STRING-EQUAL?>,
<SYMBOL-HASH, EQ?>, and <TREE-HASH, ALIKEV?>.

Weak Tables.  A table is a list of associations - pairs of
keys and values.  If the key is garbage, i.e., is no longer accessible,
I would like the association (i.e. the pointer to the value) to go away.  
This is not implementable in the current scheme.

Table Walking.  It is desirable (occasionally) to apply a procedure to
each <key,value> pair in a table.  This is possible in the current 
scheme only if you put a layer on top of the table package.

Generators.  I often write the following idiom:

        (or (table-entry table key)
            (set (table-entry table key) (make-value key)))

This would be eliminated by the provision of a generator procedure
to be called in the case when there is no value associated
with the key in a call to TABLE-ENTRY.

Before someone goes out and blindly writes a new table package,
I'd like to hear more feedback.  Are these idioms you use a lot?
Are there other ideas you really need?  Is this even the proper 
abstraction? 

                                --- Jonathan

-------

Date: 26 Mar 86 07:13:32 EST (Wed)
From: Larry Hunter <[email-masked]>
Subject: tables
To: [email-masked]
Cc: [email-masked]

I agree that it would be nice to be able to specify the hash and comparison
functions.  The nonstandard stuff I use is a table-walker, an
association-list to table conversion (and vice versa) and, for my own
perverse reasons, I sometimes need to get back a random (not arbitrary)
element from a table.

One thing that bothers me is that table-entries that have never been
defined return nil.  I often want to discriminate between values that
have never been set and ones that have been set to nil.  Shouldn't
uninitialized table-entries return some undefined value instead of nil?

                                                 Larry
-------

Date: 26 Mar 86 10:54:44 EST (Wed)
From: Jonathan Young <[email-masked]>
To: [email-masked]
Cc: [email-masked]
Subject:  Tables returning nil if no value

    One thing that bothers me is that table-entries that have never been
    defined return nil.  I often want to discriminate between values that
    have never been set and ones that have been set to nil.  Shouldn't
    uninitialized table-entries return some undefined value instead of nil?

The problem is that *any* value could be stored in the table, including
"some undefined value".

The correct solution is to return multiple values such as the following:

        (table-entry table key) => found?, value

where found? is true if there was a <key,value> pair in the table and
value is undefined if found? is false.

Any votes for this system?  Against?  

                                --- Jonathan

Date:           Wed, 26 Mar 86 11:28:56 PST
From: Scott Turner <[email-masked]>
To: Jonathan Young <[email-masked]>
Cc: [email-masked]
Subject:        Re: Tables returning nil if no value

>     One thing that bothers me is that table-entries that have never been
>     defined return nil.  I often want to discriminate between values that
>     have never been set and ones that have been set to nil.  Shouldn't
>     uninitialized table-entries return some undefined value instead of nil?

> The problem is that *any* value could be stored in the table, including
> "some undefined value".

> The correct solution is to return multiple values such as the following:

>         (table-entry table key) => found?, value

I hesitate to offer an alternative to "the correct solution", but in our
hash table (HT) package here at UCLA, we have nil as the undefined value and
an additional function, HT:KEY? that returns true if the key is defined in
the table (even with a nil value) and false otherwise.  This has the advantage
that you can ignore the defined/undefined issue if you'd like.  In general,
our solution has been to have the application (built on top of HT) decide
about the defined/undefined question.

As far as what functions I'd like in a table package, here are the ones
that we have in HT:

;  Functions intended for use by mortal beings
;
;  HT:CREATE              HT?               HT:REMOVE       HT:PAIRS
;  HT:ENTRY               HT:PAIR           FREE            HT:KEY?
;  HT:WALK                DUMP              HT:KEYS         COPY
;  HT:MAP                 HT:ADD            HT:RECORDS      HT:COMBINE
;  HT:FIND-ENTRY          HT:UPDATE         HT:COUNT

The unobvious ones:

        HT:FIND-ENTRY -- returns the first pair that satisfies a predicate.
        HT:ADD/UPDATE -- add assumes the key is new, update the key is old.

DUMP, FREE and COPY are generic operations defined on many of the packages
we have here.   DUMP is a debug print-out, FREE releases an object to
storage management, and COPY returns a copy of the object.  COPY in
particular is ill-defined and is slowly going away.

Ashwin has a copy of this code if you'd care to look at it.  It is largely
based on the original HT code.  The default hashing function is OBJECT-HASH,
but the user can define another (as well as the equality predicate) in the
call to HT:CREATE.

                                        -- Scott Turner

Date: Wed, 26 Mar 86 14:09:43 EST
From: Jonathan A Rees <[email-masked]>
Subject:  Tables returning nil if no value
To: [email-masked]
Cc: [email-masked], [email-masked]

Here's what I had in mind when I added tables:  I really wanted them to
be extremely minimal, with very little structure.  I wanted them to be
as simple and primitive as pairs.  They were to be viewed as primitive
components out of which one could build higher-level abstractions.  My
inspiration was SNOBOL4's tables.  I didn't like the idea of having a
zillion bells and whistles like in Common Lisp, or worse, Zetalisp.  It
was always my intent that the tables created by MAKE-TABLE would be weak
tables, and they clearly could be; that's why there's no TABLE-WALK.  I
never got around to implementing them that way, however.

I'm not saying this is the best way for this feature to be, I'm just
saying it's what I had in mind.

The correct way to deal with lookup failure is NOT to return a second
found? value, but instead to either build failure into the language (as
in Prolog, Snobol, or Icon, or zetalisp's condition system), or to pass
multiple continuations (like PDP-10 assembly language programmers do):

  (table-entry table key
	       (lambda (val) ... success ...)
	       (lambda () ... failure ...))

Given that neither of these alternatives seems particularly realistic
(the first because it's too drastic and the second because it's just as
cumbersome as dealing with multiple values), I think the way it is is
better.

From: [email-masked] (John R. Ellis)
Date: 26 Mar 1986 1222-PST (Wednesday)
To: Jonathan Young <[email-masked]>
Cc: [email-masked], [email-masked]
Subject: Re: Tables returning nil if no value

    The problem is that *any* value could be stored in the table, including
    "some undefined value".
    
The typical solution is to have a unique value defined in the table
interface, say *table-undefined*, which is used only for undefined
table entries.   There is no practical reason to store
*table-undefined* in a table as an explicit value.

The minor advantage of *table-undefined* compared with returning two
values is that it makes many applications simpler and slighlty more
efficient.   These applications will look up a key's value and compare
it for equality with some value they're holding; this test is a little
easier if *table-undefined* is used as sentinel value, since there will
be only one test instead of two.  

Date: 27 Mar 86 11:38:39 EST (Thu)
From: Ashwin Ram <[email-masked]>
Subject: Re: Tables returning nil if no value
To: Jonathan Young <[email-masked]>
Cc: [email-masked]

    The correct solution is to return multiple values such as the following:
    
            (table-entry table key) => found?, value
    
    where found? is true if there was a <key,value> pair in the table and
    value is undefined if found? is false.

I don't think this solution is "correct", since it violates the semantics
we intuitively expect.  Given a table T, a key K and a value V, we would,
in your scheme, have:

            (set (table-entry T K) V)
            (table-entry T K) ==> something not EQ? to V

I would vote for a predicate TABLE-ENTRY? (like Scott's HT:KEY?) which checks
whether K has an association in T.

I liked your idea of generator functions.  The user may then choose to return
NIL if s/he then wishes.  However, this is obviously orthogonal to the "found?"
problem.  Well, maybe it's not so obvious.  If a table is used as a data
structure common to two modules in a program, there is no a priori way for
the one to know that an "undefined value" (as Ellis suggested) is in fact not
a legitimate value stored in the table by the other.  The only way it could do
this is to test if the "undefined value" belonged to the co-domain of the
function being implemented, which requires an extra test anyway (and one that
is presumably more complex than null?).  Alternatively, the table package
can provide a predicate like valid-undefined-value?, which gets us right
back where we started.

-- Ashwin.

-------

Date: 27 Mar 86 11:49:29 EST (Thu)
From: Ashwin Ram <[email-masked]>
Subject: Re: Tables
To: Jonathan Young <[email-masked]>
Cc: [email-masked]

    Weak Tables.  A table is a list of associations - pairs of
    keys and values.  If the key is garbage, i.e., is no longer accessible,
    I would like the association (i.e. the pointer to the value) to go away.  
    This is not implementable in the current scheme.
    
The tables used by OBJECT-HASH/OBJECT-UNHASH are weak tables, and it isn't
too hard to implement similar tables for yourself if you really need them.
I agree, however, that they should be part of the abstraction.

    Table Walking.  It is desirable (occasionally) to apply a procedure to
    each <key,value> pair in a table.  This is possible in the current 
    scheme only if you put a layer on top of the table package.
    
Or if you used WALK-TABLE.  Unreleased, but that never stopped me from using
it.

    Before someone goes out and blindly writes a new table package,
    I'd like to hear more feedback.  Are these idioms you use a lot?
    Are there other ideas you really need?  Is this even the proper 
    abstraction? 
                                    --- Jonathan

I think I agree with Rees ["I really wanted them to be extremely minimal, with
very little structure.  I wanted them to be as simple and primitive as pairs.
They were to be viewed as primitive components out of which one could build
higher-level abstractions"].  I think Scott's HT package is great (except that
it doesn't implement weak tables), but I find it somewhat "big".  I never
use more than 25% of the functions provided.  Since most can be built up on
top of the 25% or so that are "really primitive" very easily in an particular
application, my vote lies with a good abstraction that is reasonably "small".

-- Ashwin.

    
-------

Date: 27 Mar 86 12:02:01 EST (Thu)
From: Ashwin Ram <[email-masked]>
Subject: Re: Tables returning nil if no value
To: Scott Turner <[email-masked]>
Cc: [email-masked]

    I hesitate to offer an alternative to "the correct solution", but in our
    hash table (HT) package here at UCLA, we have nil as the undefined value and
    an additional function, HT:KEY? that returns true if the key is defined in
    the table (even with a nil value) and false otherwise.  This has the advantage
    that you can ignore the defined/undefined issue if you'd like.  In general,
    our solution has been to have the application (built on top of HT) decide
    about the defined/undefined question.
    
I agree.  My philosophy is always to let the user decide about the sticky
issues, while providing a clean way to implement the outcome of the decision.

                               ...The default hashing function is OBJECT-HASH,
    but the user can define another (as well as the equality predicate) in the
    call to HT:CREATE.
    
This is also in concord with my philosophy.  Provide nice defaults but never
sacrifice extensibility.  For the HT problem, the hashing and equality functions
are the obvious examples of this.  (I was surprised not to see functions
in your list that get the hashing and equality functions for an HT.)

    The unobvious ones:
    
            HT:FIND-ENTRY -- returns the first pair that satisfies a predicate.
            HT:ADD/UPDATE -- add assumes the key is new, update the key is old.

HT:FIND-ENTRY (along with HT:MAP) don't seem to make much sense.  What does
"the FIRST pair" mean?  What significance would the order of entries returned
by HT:MAP have?  Entries in a table don't have any endogenous order.  HT:WALK,
by contrast, is fine (if you're into side-effects).  It might be useful to
make HT:MAP return a table (in the obvious way, so that (COPY ht) is just
(HT:MAP IDENTITY ht)).

-- Ashwin.
-------

Date: Mon, 31 Mar 1986  01:51 EST
Sender: [email-masked]
From: "Scott E. Fahlman" <[email-masked]>
To: [email-masked]
Cc: [email-masked], [email-masked], [email-masked]
Subject: non-list arguments

It certainly doesn't work in Common Lisp because lambda lists don't do
destructuring in Common Lisp (though the argument lists for macros do
destructure).

Also, in Common Lisp you'd have to say "(function (lambda ...))".

-- Scott

From: [email-masked] (John R. Ellis)
Date: 30 Mar 1986 2219-PST (Sunday)
To: Ashwin Ram <[email-masked]>
Cc: [email-masked], Jonathan Young <[email-masked]>
Subject: Re: Tables returning nil if no value

Date: 30 Mar 86 22:19 PST
From: [email-masked]
Subject: non-list arguments
To: [email-masked], [email-masked]
Cc: [email-masked], [email-masked]

(apply (lambda (x . y) (+ x y)) (cons 1 2))

Does this nescesarily work in Scheme?  How about T?  Common-Lisp?
(Example due to Ken Kahn)

Date: 30 Mar 86 23:33 PST
From: [email-masked]
Subject: Re: non-list arguments
To: [email-masked]
Cc: [email-masked], [email-masked], [email-masked],
        [email-masked], [email-masked]

	It certainly doesn't work in Common Lisp because lambda lists don't do
	destructuring in Common Lisp (though the argument lists for macros do
	destructure).

	Also, in Common Lisp you'd have to say "(function (lambda ...))".

OK, for Common Lisp how about: 

(apply #'(lambda (x &rest y) (+ x y)) (cons 1 2))

In other words, "&rest" is all the destructuring I need to ask this
question.  "." is simply the cleaner way one can write it in the
Schemes.

(Sorry for the redundant scheme mailing addresses.  I'm having some mail
returned and I don't know which address is causing the problem)

Date: 31 Mar 1986  09:33 EST (Mon)
From: Bill Rozas <[email-masked]>
To: [email-masked]
Cc: [email-masked], [email-masked], [email-masked]
Subject: non-list arguments

The Revised Revised Report on Scheme states that the second argument
(2 argument form) to APPLY must be a "proper" list.  Individual
implementations are free to extend the semantics of APPLY.

APPLY in MIT Scheme signals an error when given somthing other than a
"proper" list.

Date: Mon, 31 Mar 1986  02:49 EST
Sender: [email-masked]
From: "Scott E. Fahlman" <[email-masked]>
To: [email-masked]
Cc: [email-masked], [email-masked], [email-masked],
        [email-masked]
Subject: non-list arguments

    OK, for Common Lisp how about: 

    (apply #'(lambda (x &rest y) (+ x y)) (cons 1 2))

Ah, I see what you're driving at now.

No this is not guaranteed to work in Common Lisp in my opinion.  The
second argument to apply is a list of operands.  In this case, you've
got a list of one operand, 1, with a particularly ugly terminator, so
the rest arg, Y, should end up bound to NIL.  Some implementations may
get this "right" by accident, but in most of them the Apply takes apart
the operand list and simulates a call to the function.  The lambda would
be called with only one arg, and the rest arg would be NIL.  The 2 has
been discarded before the function ever gets called.

-- Scott

Date: Mon, 31 Mar 86 04:05:03 EST
From: Alan Bawden <[email-masked]>
Subject:  non-list arguments
To: [email-masked], [email-masked]
Cc: [email-masked], [email-masked], [email-masked]

    Date: 30 Mar 86 22:19 PST
    From: Miller.pa at Xerox
    (apply (lambda (x . y) (+ x y)) (cons 1 2))

    Date: Mon, 31 Mar 1986  01:51 EST
    From: Scott E. Fahlman <Fahlman at C.CS.CMU.EDU>
    It certainly doesn't work in Common Lisp because lambda lists don't do
    destructuring in Common Lisp (though the argument lists for macros do
    destructure).

    Also, in Common Lisp you'd have to say "(function (lambda ...))".

I would translate the question into Common Lisp as:

  (apply (function (lambda (x &rest y) (+ x y))) (cons 1 2))

which eliminates the question of "descructuring", but I would guess still
preserves Ken Kahn's original question.  A strict reading of the Common
Lisp book would not require this to work, as far as I can see.  An
implementor would seem to be within his rights to insist that the last
argument to APPLY always be a proper list.

Date: Sun, 30 Mar 86 16:23:54 EST
From: Jonathan A Rees <[email-masked]>
Subject:  Tables returning nil if no value
To: [email-masked]
Cc: [email-masked], [email-masked]

    Date: 27 Mar 86 11:38:39 EST (Thu)
    From: Ashwin Ram <ram at YALE.ARPA>

    I would vote for a predicate TABLE-ENTRY? (like Scott's HT:KEY?) which checks
    whether K has an association in T.

This sort of conflicts with my design which for (set (table-entry foo
bar) '#f) deletes bar's entry in the table; if there's a possibility of
having an entry of #f being different from no entry, then you have to
have a separate deletion primitive.  Things get out of hand quickly --
once you have one feature you want a zillion others.  I say nip them in
the bud.

Jonathan.

Date:           Mon, 31 Mar 86 13:58:46 PST
From: Uri Zernik <[email-masked]>
To: [email-masked]
Subject:        please take me off the list

thanks, uri

Date: Mon, 31 Mar 86 16:52:27 EST
From: Jonathan A Rees <[email-masked]>
Subject:  non-list arguments
To: [email-masked]
Cc: [email-masked], [email-masked], [email-masked],
        [email-masked]

    Date: Mon, 31 Mar 1986  01:51 EST
    From: Scott E. Fahlman <Fahlman at C.CS.CMU.EDU>

    It certainly doesn't work in Common Lisp because lambda lists don't do
    destructuring in Common Lisp (though the argument lists for macros do
    destructure).

Scheme and T don't have lambda list destructuring; they just use "."
instead of "&rest".

I haven't checked the manuals for T or Scheme but it was certainly the
intent that the list passed to APPLY is unrelated to the list that a
rest-parameter gets bound to.  The rest-list should always be freshly
consed, so e.g. the procedure LIST is equivalent to (LAMBDA X X).  The
last argument to APPLY must be a proper list.  I think Common Lisp takes
the same stance.

Date: 31 Mar 86 18:11:20 EST (Mon)
From: Ashwin Ram <[email-masked]>
Subject: Re: Tables returning nil if no value
To: [email-masked] (John R. Ellis)
Cc: [email-masked], [email-masked]

        If a table is used as a data structure common to two modules in a
        program, there is no a priori way for the one to know that an
        "undefined value" (as Ellis suggested) is in fact not a legitimate
        value stored in the table by the other.  
        
    I don't understand.  The Table package (interface, module, abstraction,
    ...) defines and exports a constant of unique type called
    UndefinedTableValue.   By the contract of the interface, no one stores
    that value in the table (why should they?).   Everyone knows for sure
    that UndefinedTableValue is not a legitimate table value.   So there
    isn't any possiblity of confusing UndefinedTableValue with any other
    value.

As I hinted at in my original message, the UndefinedTableValue solution
is fine but reduces to the TABLE-ENTRY-EXISTS? solution.  For consider what
the program that is handed UndefinedTableValue in response to a TABLE-ENTRY
call must do in order to detect that there was no proper value stored in the
table under that key.  It could either:

    a - Check that the returned value is *not* part of the co-domain of the
        function that the table implements, and hence is an "undefined table
        value", but writing (BELONGS-TO? V SOME-FUNCTIONAL-DOMAIN) is hard
        in general.

    b - Check that the returned value is the constant UndefinedTableValue
        (which is specified by the particular table package the user is using).
        However, (EQ? V 'UndefinedTableValue) reduces (i) portability of the
        code to other table packages, and (ii) transparency of the table
        implementation to the user program.

Thus the user would need to "hide" the "constant of unique type" in an
implementation-independent way, as follows:

    c - Define a predicate (UNDEFINED-TABLE-VALUE? V) which returns #t only
        to the constant UndefinedTableValue and #f to all other objects (by
        checking if V is EQ? to UndefinedTableValue).

But this, of course, is identical to the TABLE-ENTRY-EXISTS? solution (which
has the minor advantage of requiring one less function call).  It would be nice
if the table package provided this as part of the interface/module/abstraction
for the reasons outlined in b above.

-- Ashwin.

-------

Date: 31 Mar 86 18:45:15 EST (Mon)
From: Ashwin Ram <[email-masked]>
Subject: Re: Tables returning nil if no value
To: [email-masked]
Cc: [email-masked], [email-masked]

        Date: 27 Mar 86 11:38:39 EST (Thu)
        From: Ashwin Ram <ram at YALE.ARPA>
    
        I would vote for a predicate TABLE-ENTRY? (like Scott's HT:KEY?) which checks
        whether K has an association in T.
    
    This sort of conflicts with my design which for (set (table-entry foo
    bar) '#f) deletes bar's entry in the table; if there's a possibility of
    having an entry of #f being different from no entry, then you have to
    have a separate deletion primitive.  Things get out of hand quickly --
    once you have one feature you want a zillion others.  I say nip them in
    the bud.
    
    Jonathan.

    
In practical terms I agree with you.  I've been using tables (and even property
lists) for a long time without ever being bothered by the "nil vs. doesn't-exist"
distinction.  In the few cases where it matters, it's easy enough to write a
couple of one-liners to handle the situation.  For the same reason I'm quite
happy with ASS returning nil for non-existent entries and a cons cell otherwise,
and using these interchangeably as boolean and list values.

In the case of tables, however, a strong (theoretical) case could be made for
having a separate TABLE-ENTRY? predicate (along with the REMOVE-TABLE-ENTRY
primitive it necessitates).  I expect to hear a million voices claiming that
they would like to be able to store nil (or #f) explicitly in a table as a
value in its own right.  Making (set (table-entry tbl key) '#f) automatically
delete the entry may be problematic (for a sample, what is the size of the
table after you've entered one #t and five #f's into it?  One or six?  What
if you now set the one #t entry to #f?  Do you now have an empty table?)

One of these days we'll move on to three-valued logics and then everyone
will be happy :-).

-- Ashwin.

-------

Date: Mon, 31 Mar 86 04:05:03 EST
From: Alan Bawden <[email-masked]>
Subject:  non-list arguments
To: [email-masked], [email-masked]
Cc: [email-masked], [email-masked], [email-masked]

    Date: 30 Mar 86 22:19 PST
    From: Miller.pa at Xerox
    (apply (lambda (x . y) (+ x y)) (cons 1 2))

    Date: Mon, 31 Mar 1986  01:51 EST
    From: Scott E. Fahlman <Fahlman at C.CS.CMU.EDU>
    It certainly doesn't work in Common Lisp because lambda lists don't do
    destructuring in Common Lisp (though the argument lists for macros do
    destructure).

    Also, in Common Lisp you'd have to say "(function (lambda ...))".

I would translate the question into Common Lisp as:

  (apply (function (lambda (x &rest y) (+ x y))) (cons 1 2))

which eliminates the question of "descructuring", but I would guess still
preserves Ken Kahn's original question.  A strict reading of the Common
Lisp book would not require this to work, as far as I can see.  An
implementor would seem to be within his rights to insist that the last
argument to APPLY always be a proper list.

Date: Mon, 31 Mar 1986  01:51 EST
Sender: [email-masked]
From: "Scott E. Fahlman" <[email-masked]>
To: [email-masked]
Cc: [email-masked], [email-masked], [email-masked]
Subject: non-list arguments

It certainly doesn't work in Common Lisp because lambda lists don't do
destructuring in Common Lisp (though the argument lists for macros do
destructure).

Also, in Common Lisp you'd have to say "(function (lambda ...))".

-- Scott

Date: Mon, 31 Mar 1986  02:49 EST
Sender: [email-masked]
From: "Scott E. Fahlman" <[email-masked]>
To: [email-masked]
Cc: [email-masked], [email-masked], [email-masked],
        [email-masked]
Subject: non-list arguments

    OK, for Common Lisp how about: 

    (apply #'(lambda (x &rest y) (+ x y)) (cons 1 2))

Ah, I see what you're driving at now.

No this is not guaranteed to work in Common Lisp in my opinion.  The
second argument to apply is a list of operands.  In this case, you've
got a list of one operand, 1, with a particularly ugly terminator, so
the rest arg, Y, should end up bound to NIL.  Some implementations may
get this "right" by accident, but in most of them the Apply takes apart
the operand list and simulates a call to the function.  The lambda would
be called with only one arg, and the rest arg would be NIL.  The 2 has
been discarded before the function ever gets called.

-- Scott

Date: 1 Apr 86 12:20:32 EST (Tue)
From: Larry Hunter <[email-masked]>
Subject: Nil v. UndefinedTableValue
To: [email-masked]

After bringing up the issue and listening (?) to the various replies,
I find myself inclined to side with Rees and retract my initial wish.

In situations where I want to know if an entry has been actually set
to nil, there are a variety of approaches I can use on top of the vanilla
package (eg, maintain a list of keys as a table entry, cons each entry
to some symbol thereby simulating multiple value return, etc).  If I
don't care, I can use the vanilla package.  I have been doing this all
along, and, given T's philosophy of providing the minimum sufficient
functionality and leaving the driving to us, I don't see any compelling
reason to change.

My comments about having hooks for providing hashing and equality
functions still stand, tho.

                                            Larry
-------

Date: Tue, 1 Apr 86 15:26 EST
From: Robert Halstead <[email-masked]>
Subject: non-list arguments
To: [email-masked]
Cc: [email-masked], [email-masked]

    I haven't checked the manuals for T or Scheme but it was certainly the
    intent that the list passed to APPLY is unrelated to the list that a
    rest-parameter gets bound to.  The rest-list should always be freshly
    consed, so e.g. the procedure LIST is equivalent to (LAMBDA X X).  The
    last argument to APPLY must be a proper list.  I think Common Lisp takes
    the same stance.

A quick scan of the Common Lisp book leaves the question unresolved;
maybe Guy Steele could comment.  However, I have seen object-oriented
programs where this would probably lead to less than the most efficient
implementation.  Suppose we represent objects by procedures and call
them using the general form

	(<object> <key> <other stuff>)

where the <key> indicates the operation to be performed on <object> and
<other stuff> (possibly empty) consists of operation-dependent
parameters.  One way to code up such an object is as

	(lambda (key . rest)
	  (cond ((eq? key 'foo) (apply f1 rest))
		((eq? key 'bar) (apply f2 (cons 'barbar rest)))
		...))

where we use the key to pick a function (or new object) to forward the
request to, on the assumption that somebody at the end of the chain will
actually use the rest of the original arguments.  Putting an extra list
copy in at each forwarding step seems mildly expensive and somewhat
unnecessary.  I don't think that LAMBDA should be prohibited from
checking that its argument is a true list, or from copying it over, but
I also don't think it should be required to.  Why is it important for
the list of rest arguments to be copied over when we don't, for example,
expect quoted lists to be copied over every time an expression '(...) is
evaluated?

To summarize, I think it should be permissible for

	(eq? ((lambda x x) l) l)

to return true, but it should not be a requirement.  Furthermore, it
should be permissible for an implementation to report an error if l in
the above expression is not a true list, but an implementation should
not be required to do so.  Of course, it would still be true that

	((lambda x x) 3 4 5)

would return a freshly consed list, just like (list 3 4 5).  An
interesting question:  do people expect (apply list l) to return a
top-level copy of l?					-b.

Date: Mon, 31 Mar 1986  01:51 EST
Sender: [email-masked]
From: "Scott E. Fahlman" <[email-masked]>
To: [email-masked]
Cc: [email-masked], [email-masked], [email-masked]
Subject: non-list arguments

It certainly doesn't work in Common Lisp because lambda lists don't do
destructuring in Common Lisp (though the argument lists for macros do
destructure).

Also, in Common Lisp you'd have to say "(function (lambda ...))".

-- Scott

Date: Thu, 3 Apr 86 10:36 EST
From: Guy Steele <[email-masked]>
Subject: non-list arguments
To: [email-masked], [email-masked]
Cc: [email-masked], [email-masked], [email-masked]

	I haven't checked the manuals for T or Scheme but it was certainly the
	intent that the list passed to APPLY is unrelated to the list that a
	rest-parameter gets bound to.  The rest-list should always be freshly
	consed, so e.g. the procedure LIST is equivalent to (LAMBDA X X).  The
	last argument to APPLY must be a proper list.  I think Common Lisp takes
	the same stance.
    
    A quick scan of the Common Lisp book leaves the question unresolved;
    maybe Guy Steele could comment.

This question came up recently on the Common-Lisp mailing list.  The
unofficial consensus, as I understood it, was that the book is vague on
this point and requires correcting; that the last argument to APPLY
should be a proper list; and that a list passed as an argument to APPLY
might or might not share list structure with an eventual &REST argument
resulting from the application.

--Guy

Date: Thu, 3 Apr 86 10:36 EST
From: Guy Steele <[email-masked]>
Subject: non-list arguments
To: [email-masked], [email-masked]
Cc: [email-masked], [email-masked], [email-masked]

	I haven't checked the manuals for T or Scheme but it was certainly the
	intent that the list passed to APPLY is unrelated to the list that a
	rest-parameter gets bound to.  The rest-list should always be freshly
	consed, so e.g. the procedure LIST is equivalent to (LAMBDA X X).  The
	last argument to APPLY must be a proper list.  I think Common Lisp takes
	the same stance.
    
    A quick scan of the Common Lisp book leaves the question unresolved;
    maybe Guy Steele could comment.

This question came up recently on the Common-Lisp mailing list.  The
unofficial consensus, as I understood it, was that the book is vague on
this point and requires correcting; that the last argument to APPLY
should be a proper list; and that a list passed as an argument to APPLY
might or might not share list structure with an eventual &REST argument
resulting from the application.

--Guy

Date: 4 Apr 86 12:05:25 EST (Fri)
From: Ashwin Ram <[email-masked]>
Subject: Kudos
To: [email-masked]

What do you think this will return (i.e., T or NIL):

   (= (factorial 1999) (/ (factorial 2000) 2000))

where FACTORIAL is the obvious non-tail-recursive implementation of the
factorial function?  (Remember that (factorial 2000) is a VERY big number.)

Well, I tried it.  After three garbage collections, it finally returned with
--- did you guess it? --- T !!!  Hats off to the T implementors!

Ashwin.

-------