[comp.lang.lisp] Greatly exaggerated reports of Lisp's demise

meehan@src.dec.com (Jim Meehan) (02/13/91)

I'm amazed at the recent spate of Lisp-bashing on comp.lang.lisp, and
how few replies there have been either to defend Common Lisp or to
explain misunderstandings in the complaints.  CL is not without its
faults, but it ain't COBOL, either ;-)

At the company where I used to work, we used UCI LISP for a year, T
for a couple of years, and Common Lisp for the past six years.  CL
hasn't died.  On the contrary, it survived on a large scale where lots
of other dialects did not, including UCI LISP, about which I can speak
authoritatively, and maybe Franz Lisp, about which I cannot.  Once T
got call/cc, it became a syntactic variant of Scheme, so from my
perspective, there were only two Lisp dialects of interest:  Common
Lisp, which was the dialect of choice for an industrial-strength,
commercially supported Lisp, and Scheme, which was small and elegant
but not yet adequate for large-scale commercial software.  (Je ne parle
pas ML.)

Erann Gat asked why the CL committee decided "not to include
call-with-current-continuation."  I wasn't on the committee, but my
impression is that 1984 Common Lisp predates call/cc, or at least it
was thought to be difficult or expensive to implement (or that most
CL programmers would abuse it :-), but it would be interesting to hear
directly from them.  Of course, what's difficult one year may be less
difficult the next; witness ephemeral GC on stock hardware.

I don't know why call/cc didn't make it into CLtL II.  Barry Margolin
says "it wasn't demmed necessary."  In our applications (30% natural
language processing, 70% systems programming), I'm not sure we would
ever have used call/cc, but we lived and breathed closures, so I think
the commitee made the right choice.

But it would not trouble me to see call/cc added.  It does trouble
me to see the new LOOP macro added.  It's yet another embedded
sublanguage (FORMAT is another one, perhaps "the" other one).  But maybe
the obvious thing for the committee to do is to formalize the notion
of libraries: collections of functions, macros, and global variables
that serve particular needs, so that if you don't want them, you don't
pay for them.  As an AI programmer, I'd have relegated all those math
functions (branch cuts, etc.) to some distant shore, too.  The object
system is crucial; the pretty-printer is not.

On the other hand, several commercially available implementations provide a
system-building option for excluding the pieces of CL that you don't
use or want.  Of course, if you write code that does (funcall (intern
(format nil ...))) or (eval ...)  or (compile ...)  at runtime, all
bets are off, but then you're not writing commercial code :-).

Of course, having the wealth of choices for writing the same code is
not always a good thing.  We had an in-house "Common Lisp style sheet"
that included, among other advice, a list of "forbidden forms," whose
use you would have to justify to the company's Programming Czar, who
almost never let you get away with anything.  The Forbidden List
included DO*, RPLACA, RPLACD, PROGV, any C{A,D}R function with more
than two letters between the C and the R (and those only for plists),
and others.  There was also a Restricted-Use List, including APPEND,
PROGN, BLOCK, etc., where there were specific cases where they could
be used, but not elsewhere.  The programmers liked this.  It promoted
useful discussion, especially among Lisp newcomers, it enhanced
readability, and it spared them age-old hassles.

On the issue of REQUIRE, we viewed this as a simple mechanism
appropriate for simple programs.  With large-scale programming, you
need a whole suite of program-management tools, so I don't think that
the committee was being "a little unfriendly" as Vance Maverick notes,
but simply realistic.  We designed and built our own, and stopped using
REQUIRE completely.  Defsystem was just the beginning; the majority
of the code was for tracking the various dependencies within and between
"systems": read-time, compile-time, load-time (both compiled and
interpreted), eval-time, and run-time dependencies.  I don't know how
many other shops built similar elaborate environmental tools, but I'm
not surprised there's nothing in the CL standard, because there probably
isn't much general agreement based on a lot of real-world experience
in this area.  Maybe some day ...

On the issue of finding out what a class' instances are, or what slots
a structure has, I'll take the hard line and say there's good reason
NOT to provide that information.  As a couple of people have pointed
out, you can build classes that do keep track of their instances, but
then you have to worry about GC.  In other words, you normally don't
want to pay for this.

On a slightly more abstract level, consider that it is often desirable
to make instances and structures as lightweight as possible.  For
example, you'd hardly want to keep track of all the numbers in your
program; you assume that even if they're not "immediate" data, they
won't cost you much, and they'll go away after you've stopped using
them.  All objects should share this philosophy, especially when
performance is an issue.  If you need to fiddle with the underlying
representation, you're doing something strange, so it's no wonder that
constructing slot-names at run-time is awkward; it should be.  (I *told*
you this was the hard line.)


Jim Meehan

barmar@think.com (Barry Margolin) (02/13/91)

In article <1991Feb12.122415.23035@src.dec.com> meehan@src.dec.com (Jim Meehan) writes:
>But it would not trouble me to see call/cc added.  It does trouble
>me to see the new LOOP macro added.  It's yet another embedded
>sublanguage (FORMAT is another one, perhaps "the" other one).

Look at it this way: we didn't *add* it to the language, we simply
acknowledged that it is being used extensively within the CL community.
This is precisely what standards committees are supposed to do.  I can
remember periods when someone would ask how to FTP a public domain
implementation of LOOP, and the major commercial Common Lisp
implementations (including Symbolics, Lucid, and Franz) all include a LOOP
implementation.  The problem we were addressing was that there was nothing
forcing all these LOOP implementations to be consistent with each other,
which just causes frustration for the programmers who wish to use it (but
they still use it anyway).  In the process of acknowledging it by adding
the construct to the language, we also cleaned up the specification
(resolving some ambiguities, removing unnecessary generality (e.g. MIT LOOP
is more flexible about the placement of some clauses), and adding features
necessary for consistency with the rest of the language (e.g. CL type
declarations and hash table iteration)).

>  But maybe
>the obvious thing for the committee to do is to formalize the notion
>of libraries: collections of functions, macros, and global variables
>that serve particular needs, so that if you don't want them, you don't
>pay for them.  

Maybe "obvious" to you, but not to everyone.  The idea of optional sections
of the standard was discussed quite a bit, especially regarding the large
features that were added by X3J13 (in case anyone cares, I was a member of
the small contingent in favor of making certain features optional).  We
discussed having some parts of the spec effectively say "the fancy LOOP
form isn't required, but if it is provided here is the specification."  We
also talked about maintaining a repository of public domain versions of
some of these libraries, so that users of implementations that don't
provide them could download the code.  And we discussed defining explicit
subset versions of the language.  We eventually decided against these
ideas, mostly because we simply weren't able to define the boundaries and
don't have the resources to maintain the library.  Note that there is
nothing preventing an implementation from leaving out facilities, so long
as the documentation clearly notes that such differences exist.  If there's
a significant market for LOOPless Common Lisps then there will probably be
vendors selling them; if there isn't much of a demand for it, then X3J13
would have wasted its time defining the subset.

Note that even if we had defined many functions as optional, we probably
would have reserved the names in the COMMON-LISP package, so that programs
originally implemented using a subset wouldn't have problems when loaded
into a full language implementation, so optionalizing things probably
wouldn't have helped cut down the size of the Common Lisp namespace.
Perhaps if we'd relegated them to their own packages, but I think there was
resistance to usurping too much of the package namespace.

>		As an AI programmer, I'd have relegated all those math
>functions (branch cuts, etc.) to some distant shore, too.  

Here's an example of why it's hard to define subsets: "All those math
functions" includes ordinary arithmetic.  I suspect even AI programs need
to add integers once in a while.

>							    The object
>system is crucial; the pretty-printer is not.

Hmm, I think the pretty-printer interface was needed mostly to allow
object-oriented programmers to control the pretty printing of their
objects, since the built-in types already interact with it.

>Of course, having the wealth of choices for writing the same code is
>not always a good thing.  We had an in-house "Common Lisp style sheet"
>that included, among other advice, a list of "forbidden forms," whose
>use you would have to justify to the company's Programming Czar, who
>almost never let you get away with anything.  The Forbidden List
>included DO*, RPLACA, RPLACD, PROGV, any C{A,D}R function with more
>than two letters between the C and the R (and those only for plists),
>and others.  There was also a Restricted-Use List, including APPEND,
>PROGN, BLOCK, etc., where there were specific cases where they could
>be used, but not elsewhere.  The programmers liked this.  It promoted
>useful discussion, especially among Lisp newcomers, it enhanced
>readability, and it spared them age-old hassles.

When I worked at Honeywell programming Multics we had a similar guide for
PL/I, and I thought this was a great policy.  It was mostly a guideline,
but it assisted during the required code audit by another programmer (there
were some stronger requirements for kernel and security-related code).

>On the issue of REQUIRE, we viewed this as a simple mechanism
>appropriate for simple programs.  With large-scale programming, you
>need a whole suite of program-management tools, so I don't think that
>the committee was being "a little unfriendly" as Vance Maverick notes,
>but simply realistic.  

Thank you for a user's viewpoint.  Another way to describe X3J13's view
about REQUIRE is that it lulled many users into thinking that it was more
than it really was.  Given the generality of the rest of the language, I
can understand why someone might expect REQUIRE to be more than an
extremely limited facility.
--
Barry Margolin, Thinking Machines Corp.

barmar@think.com
{uunet,harvard}!think!barmar

jeff@aiai.ed.ac.uk (Jeff Dalton) (02/13/91)

In article <1991Feb12.122415.23035@src.dec.com> meehan@src.dec.com (Jim Meehan) writes:
>I'm amazed at the recent spate of Lisp-bashing on comp.lang.lisp, and
>how few replies there have been either to defend Common Lisp or to
>explain misunderstandings in the complaints.  CL is not without its
>faults, but it ain't COBOL, either ;-)

Humm.  I've tried to defend Common Lisp to some extent and to explain
some misunderstandings.  But I also think it's useful to identify
problems so that implementors are encouraged to do something about
them.  So in a sense I'm on both sides.

>Erann Gat asked why the CL committee decided "not to include
>call-with-current-continuation."  I wasn't on the committee, but my
>impression is that 1984 Common Lisp predates call/cc, or at least it
>was thought to be difficult or expensive to implement (or that most
>CL programmers would abuse it :-), but it would be interesting to hear
>directly from them.  Of course, what's difficult one year may be less
>difficult the next; witness ephemeral GC on stock hardware.

I think there are good reasons not to have call/cc.

Scheme wants call/cc because it's a single, powerful mechanism that
subsumes a number of less general ones; and that's the kind of
language Scheme is.  Efficient implementation techniques have been
developed, and so I think the sort of progress that allowed ephemeral
GC on stock hardware has to a large extent already occurred.

However, the presence of call/cc does make it harder to say that some
dynamic context has definitely been exited.  This has a number of
consequences, such as making it less clear how dynamic scoping should
work and making certain idioms less reasonable to use.  For example,
it's common to nreverse a result, but what if the function that
produced it might return more than once?  (This observation about
nreverse / reverse! is not original with me; I got it from a talk by
Will Clinger or someone at the Snowbird L&FP conference.)  

So there are certain conceptual and practical consequences of call/cc
apart from direct efficiency issues.  I think it's more reasonable
to have such a construct in Scheme than in CL.

>But it would not trouble me to see call/cc added.  It does trouble
>me to see the new LOOP macro added.  It's yet another embedded
>sublanguage (FORMAT is another one, perhaps "the" other one).  But maybe
>the obvious thing for the committee to do is to formalize the notion
>of libraries: collections of functions, macros, and global variables
>that serve particular needs, so that if you don't want them, you don't
>pay for them. 

Another approach might be for implementations to do this.  Some people
think they can't, because CL is too deeply intertwined.  I think they
are wrong.

>Of course, having the wealth of choices for writing the same code is
>not always a good thing.  We had an in-house "Common Lisp style sheet"

This is a good idea.  Would it be possible to have a copy?  I'd
be interested in seeing what you decided.

>On the issue of finding out what a class' instances are, or what slots
>a structure has, I'll take the hard line and say there's good reason
>NOT to provide that information.  As a couple of people have pointed
>out, you can build classes that do keep track of their instances, but
>then you have to worry about GC.  In other words, you normally don't
>want to pay for this.

I agree completely on the issue of classes that keep track of their
instances (and would make as similar point about named instances),
but I think the question of defstruct slots is different.  The amount
you have to pay is fairly small, and a number of people have had to
implement this functionality themselves.  This can be annoying when
the implementation will almost always have the information somewhere.
But this sort of problem has been greatly reduced by the addition
of CLOS.

-- jeff

Jeff Dalton,                      JANET: J.Dalton@uk.ac.ed             
AI Applications Institute,        ARPA:  J.Dalton%uk.ac.ed@nsfnet-relay.ac.uk
Edinburgh University.             UUCP:  ...!ukc!ed.ac.uk!J.Dalton

jeff@aiai.ed.ac.uk (Jeff Dalton) (02/14/91)

In article <1991Feb13.051724.21056@Think.COM> barmar@think.com (Barry Margolin) writes:
>In article <1991Feb12.122415.23035@src.dec.com> meehan@src.dec.com (Jim Meehan) writes:
>>But it would not trouble me to see call/cc added.  It does trouble
>>me to see the new LOOP macro added.  It's yet another embedded
>>sublanguage (FORMAT is another one, perhaps "the" other one).
>
>Look at it this way: we didn't *add* it to the language, we simply
>acknowledged that it is being used extensively within the CL community.
>This is precisely what standards committees are supposed to do.  I can
>remember periods when someone would ask how to FTP a public domain
>implementation of LOOP, and the major commercial Common Lisp
>implementations (including Symbolics, Lucid, and Franz) all include a LOOP
>implementation. 

I thought there was a file at MIT that everyone was supposed to be
able to use.  It was the standard implementation, at least in intent.

I agree that it helps to agree on a standard specification.  However,
I also think it makes sense to divide the language at least conceptually
into parts.  Core language + libraries is a well-stablished way to
make such divisions.

Moreover, I think that can be done with the language as currently
defined.  Part of the reason some people think CL is a giant,
monolithic language is due to how it's been presented and to how
implementations have worked -- ie, for essentially historical reasons.
Indeed, the current "definitions" of CL (CLtL, the X3J13 draft)
can be understood in a more structured, less monolithic way.
I've tired to make this point in several articles, because in my
experience this sort of criticism is very frequent and I think it is,
in part, a misunderstanding.

However, Common Lisp could be better in this respect.

>Maybe "obvious" to you, but not to everyone. 

That's a pretty much universal problem with "obvious", I think.

>                              The idea of optional sections
>of the standard was discussed quite a bit, especially regarding the large
>features that were added by X3J13 (in case anyone cares, I was a member of
>the small contingent in favor of making certain features optional).  We
>discussed having some parts of the spec effectively say "the fancy LOOP
>form isn't required, but if it is provided here is the specification."  We
>also talked about maintaining a repository of public domain versions of
>some of these libraries, so that users of implementations that don't
>provide them could download the code.  And we discussed defining explicit
>subset versions of the language.  We eventually decided against these
>ideas, mostly because we simply weren't able to define the boundaries and
>don't have the resources to maintain the library. 

Note that making some things "library" doesn't mean thay must be
optional.  We could require that all implementations provide those
procedures.  (Sort of like "hosted" implementations of C and the
standard i/o library.)  Of course, there could also be other libraries
that aren't part of the standard.

I think subsets were rejected for several reasons, and among them:

 * It would difficult to find the right subsets, given that there
   are several ways to do it and that people have different interests.
   (This is where libraries have an advantage.  To a large extent,
   you can mix and match.)

 * It was felt that there wasn't much point to subset implementations
   because they could become full implementations by loading some pd
   code from CMU.  (Actually, they'd probably have to change the code
   a bit, or change their implementation, not just load it).

 * Subsets were associated with the idea of optional parts of the
   language.  That looked like it might be bad for users.  (The
   possible advantages to users weren't much noticed.)  And it might
   threaten the status of some of the later additions to the language.
   CLOS fans didn't want it to be optional, for example.

 * Most people in X3J13 weren't very interested in subsets.  It was
   the sort of thing _critics_ of Common Lisp kept going on about,
   and they probably wouldn't like Common Lisp no matter what
   concessions were made.

>Note that even if we had defined many functions as optional, we probably
>would have reserved the names in the COMMON-LISP package, so that programs
>originally implemented using a subset wouldn't have problems when loaded
>into a full language implementation, so optionalizing things probably
>wouldn't have helped cut down the size of the Common Lisp namespace.
>Perhaps if we'd relegated them to their own packages, but I think there was
>resistance to usurping too much of the package namespace.

I don't think that would be too much of a problem in practice.
Just as we have names in the LISP package such as STANDARD-OBJECT,
we could have package names such as STANDARD-<feature>.  Or we could,
as you suggest, have the standard library routines be in the LISP
package.

-- jd

mikeb@wdl35.wdl.loral.com (Michael H Bender) (02/16/91)

Jim Meehan writes:
   ....

   On the other hand, several commercially available implementations provide a
   system-building option for excluding the pieces of CL that you don't
   use or want.  Of course, if you write code that does (funcall (intern
   (format nil ...))) or (eval ...)  or (compile ...)  at runtime, all
   bets are off, but then you're not writing commercial code :-).

Question: As a novice Lisp programmer, I am currently designing and coding
a system with that will dynamically define new classes (in CLOS). You seem
to imply that this would not be the right approach in a deliverable system.
Why? What is the alternative? (The system is a knowledge-based shell and
has to accept new "patterns" dynamically as the user defines them.)

   Of course, having the wealth of choices for writing the same code is
   not always a good thing.  We had an in-house "Common Lisp style sheet"
   that included, among other advice, a list of "forbidden forms," whose
   use you would have to justify to the company's Programming Czar, who
   almost never let you get away with anything.  The Forbidden List
   included DO*, RPLACA, RPLACD, PROGV, any C{A,D}R function with more
   than two letters between the C and the R (and those only for plists),
   and others.  There was also a Restricted-Use List, including APPEND,
   PROGN, BLOCK, etc., where there were specific cases where they could
   be used, but not elsewhere.  The programmers liked this.  It promoted
   useful discussion, especially among Lisp newcomers, it enhanced
   readability, and it spared them age-old hassles.

Again, as a novice Lisp programmer I would love to get a copy of some
type of style sheet. Can anybody recommend one or send me a copy of
one they use in-house?

Thanks in advance, 

Mike Bender

barmar@think.com (Barry Margolin) (02/16/91)

In article <MIKEB.91Feb15133343@wdl35.wdl.loral.com> mikeb@wdl35.wdl.loral.com (Michael H Bender) writes:
>Jim Meehan writes:
>   ....
>
>   On the other hand, several commercially available implementations provide a
>   system-building option for excluding the pieces of CL that you don't
>   use or want.  Of course, if you write code that does (funcall (intern
>   (format nil ...))) or (eval ...)  or (compile ...)  at runtime, all
>   bets are off, but then you're not writing commercial code :-).
>
>Question: As a novice Lisp programmer, I am currently designing and coding
>a system with that will dynamically define new classes (in CLOS). You seem
>to imply that this would not be the right approach in a deliverable system.
>Why? What is the alternative? (The system is a knowledge-based shell and
>has to accept new "patterns" dynamically as the user defines them.)

Unfortunately, you're kind of screwed, because the functional interface to
class creation hasn't been agreed upon yet.  Ideally, what you would want
to do is to create anonymous classes, and record them in your own data
structures, rather than using constructed symbols to name them.

However, it's not quite as bad as Jim Meehan suggests.  The software for
generating delivery systems generally provide a way for you to manually
control which parts of the system should be retained and which should be
omitted, rather than depending only on an automatic tree shaker.  For
instance, if you know that you're only going to be compiling routines that
your software builds, then you may be able to determine that it will only
use a small set of macros, and all the rest may be discarded, and this
information can be provided to the system builder.

--
Barry Margolin, Thinking Machines Corp.

barmar@think.com
{uunet,harvard}!think!barmar