Why is Python popular, while Lisp and Scheme aren't?

Alexander Schmolck a.schmolck at gmx.net
Wed Nov 27 17:44:33 EST 2002


Martti Halminen <martti.halminen at kolumbus.fi> writes:

> Alexander Schmolck wrote:
> 
> > Sure, loop isn't the only iteration construct, but that doesn't really make it
> > any better, does it? Especially given that all suffer from the fact that they
> > only work on a quite small number of containers.
> 
> Given that every "container" in the language has sufficient iteration
> constructs to handle them, I don't see this as a problem.

I really have difficulties understanding why you think this lack of
polymorphism isn't (at least potentially) a problem.  That's why I brought the
ocaml example as an analogy: in ocaml you can't just do "1.0 + 1.0", although
"1 + 1" works fine. That's because you have to use different operators for
floats and ints. As there are however "sufficient constructs" for every number
type "in the language", I guess you wouldn't see a problem with that either?

> Steele's book "Common Lisp: the Language" was published in 1984; the
> biggest missing piece was CLOS.

And decent error handling, if I remember right?

> The second edition, in 1990 contained most of the stuff that ended in
> the standard.

And some stuff that didn't (like series). But anyway, you conviently ignored
my question why the fact that lisp (including pre-ansi CL) was around before
python, perl etc. should have been a *disadvantage*. According to the logic of
your argument (fast computers that mamke HLL viable are a relatively recent
development), CL should in fact have had a major competetive advantage (when
perl came out, there were already lisps that could rival fortran for numeric
code). There are of course also plenty of other reasons why being an (I
suspect you'd assume inferior) latecomer like python should be
detrimental.

> Nothing you have written has shown an "iteration class" as anything
> desirable, either. 

OK, let me try a real example. One quite powerful corollary of having a
generalized iteration protocol is that you can iterate over things that
generate items on the fly (especially useful now that python also sports
generators).

This simple example, closely based on something I used not too long ago
illustrates many of the nice features of iteration in python:

colors = "rgbkcmy"
styles = (":", "-:", "-", ".")
for line, fill, color in zip(matrix, *map(xcycle, (colors, styles))):
    plot(line, fill + color)   # append color to fill

xcycle is a self-defined utility function that forever loops over some
iterable:

def xcycle(seq):
    while 1:
        for item in seq: yield item

This will plot the rows of a matrix as lines with different color and fill
combinations. Note that in this non-contrived example, there are 5 (!)
different kinds of containers (or iterables): a matrix object, a string, a
tuple, a list and a generator. 

The list returned by map is admittedly a bit gratuitous, since for only 2
arguments I might as well have written "...xcylce(colors), xcylce(styles)",
but it provided a nice opportunity to bring in the nice *-syntax as a
shorthand for apply (which of course also works on any iterable (and is much
more readable than "apply(zip, [matrix] + map(...))")). 

The only thing I dislike is of the use of "+" to append stuff :)

How would you write the same in CL? 

Of course this example is quite trivial (note however, that it handles
different containers for "lines", such as arrays, matrix'es etc. which happen
to be different classes), but I hope it helps to illustrate the point (if not
I guess I could show you some others).


> 
> > But that is besides the point. The python ``x = cont[indx]`` will likely
> > continue to work even if I choose to replace ``cont`` with some other
> > container type later. Same for ``for i in cont: ...`` etc. This is a fairly
> > important abstraction barrier.
> 
> If I didn't misunderstand the Python Reference Manual, that would
> require stuff like __len__, __getitem__, __setitem__, __delitem__,
> __iter__ to be written by you for it to work. 

Yes, if you wrote something that would act like a container type but instead
of naming the relevant methods __iter__, __getitem__ etc.  you'd idiotically
call them , say 'frblbar' and 'qrtfds' etc., then your 'container' wouldn't
support the iteration protocol and so forth. But what are you trying to say?

> 
> > OTOH ``(let ((x (gethash key hash)) won't. T Sure, I could go along and define
> > my own generic accesor functions (CL is powerful enough for that) 
> 
> (defmethod gethash (key (hash my-hash-table-type))
>    ... whatever needed for my own stuff)
> 
> (defmethod gethash (key hash) 
>      (cl:gethash key hash));; default to normal
> 
> If that was difficult in your opinion, we have different definitions for
> difficulty.

The point was not difficulty. The reason scheme sucks so bad is not because
doing things is inherently difficult. It sucks because important fundamentals
have no standardized expression.

> It is a risk for your own code, but as you would be defining the
> semantics, however underspecified you like, that is your problem. The

I know that would not affect the core language, but that alone isn't good
enough.

> system continues to run its built-in version for its internals
> regardless of what you do in your own code (assuming you did that in
> your own package, which is the only sensible way unless you actually
> know what you are doing.)

Are there any cases where you'd know what you're doing (I thought messing with
cl: => undefined behavior, according to the standard)?

>  
> > > complex already for most people.
> > 
> > Well yes, but not sufficiently general. The for loop in python is not complex,
> > but more general in that it takes an infinite variety of container classes
> > (including lazy ones, which obviously adds expressiveness). And I should
> > suspect that people would actually like to extend loop if it weren't such a
> > nightmare (also note that e.g. emacs's (cl) LOOP *is* extended).
> 
> If you like the Python looping constructs better, it is possible to
> implement those in CL. Feel free.

Actually, I'd be tempted, if I were to do quite a bit of CL coding. That, of
course wouldn't make the problem go away (just as the fact that you can define
(various, incompatible) module and OO systems in scheme doesn't make the
problem that scheme doesn't have a module system go away). It would also not
mean that the myriad of builtin functions that work on lists or sequences
(count-if, some, etc.) or other people's code would magically start to accept
all kind of container types.

Before we run into some misunderstanding again, there is of course little
magic about 'for' as such, the nice thing about it (and 'map' and 'filter'
etc.) is just that it works for everything that support python's iteration
protocol (an ersatz of which I'd have to implement).

> 
> Hey, we agree on something! :-)

Oh, well we might agree on more that you're thinking :)

> 
> > Unified item access, setting and deletion OTOH is an abstraction and thus
> > helps to produce better code. If you disagree, try replacing all instances of
> > SETF in your code with SETQ. 
> Given that they do quite different things, I fail to see the meaning of
> this. For that matter, I prefer SETQ when handling symbols: it is less
> dangerous.


What are different things (not SETF and SETQ, I assume, since the former is
pretty much a superset of the latter and PAIP, e.g. never uses SETQ)? If you
mean setf and python's "cont[FOO] = x" etc. then I'd like to know what makes
you think so?


> > > Now what tools did Python have to define your own control flow
> > > constructs ? :-)
> 
> > Well, for some cases where lispers think you'd need macros the iteration
> > protocol and generators are actually enough (and, I guess, also better). Of
> > course there are quite a few things you can conveniently do with macros that
> > are awkward in python and CL has many cool things python hasn't -- but I
> > really think iteration and containers are better done in python.
> 
> That may be if you are happy with just that. Quite often in Lisp
> programming the datastructures are trees, graphs etc where a
> single-dimensional iteration is not what is needed, so it is not such a
> holy grail as you make it to be.

It is of course possible that usage patterns differ, so that this kind of
iteration indeed doesn't occur that often (the number of CL builtins that
follow this pattern makes me doubt that, though). 

Why it shouldn't be useful to iterate through all the leaves of
e.g. a tree, however, is really beyond me. 

BTW, I guess the ability to deep and shallow-copying unknown containers is
also not useful?

> 
> At one level it is a question of stylistical freedom: in Python you
> program in a language which looks like Guido likes it to be. In CL you
> can program in a language that looks like anything you want it to be,
> assuming you consider the effort worthwhile. Not all do.

Sure, I'm explicitly acknowledging the neatness of a proper (unlike, say cpp)
macro system above. However I also happen to think that CL's macros are not
good enough for python (in a non-derogatory way; doing them better is not
trivial), so I don't think it is not purely a difference in stylistic freedom
Guido is willing to grant.

alex



More information about the Python-list mailing list