Why don't people like lisp?

Brian Kelley bkelley at wi.mit.edu
Wed Oct 22 12:30:01 EDT 2003


Pascal Costanza wrote:

> BTW, macros are definitely more flexible and more succinct. The only 
> claim that I recall being made by Pythonistas is that macros make code 
> harder to read. The Python argument is that uniformity eases 
> readability; the Lisp argument is that a better match to the problem 
> domain eases readability. I think that...
> 
> (with-open-file (f "...")
>   ...
>   (read f)
>   ...)
> 
> ...is much clearer than...
> 
> try:
>    f=open('...')
>    ...
>    f.read()
>    ...
> finally:
>    f.close()
> 
> Why do I need to say "finally" here? Why should I even care about 
> calling close? What does this have to do with the problem I am trying to 
> solve? Do you really think it does not distract from the problem when 
> you first encounter that code and try to see the forest from the trees?
Your two examples do completely different things and the second is 
written rather poorly as f might might not exist in the finally block. 
It certainly won't if the file doesn't exist.

A better comparison would (*might*, I haven't used lisp in a while) be:

(with-open-file (f filename :direction :output :if-exists :supersede)
     (format f "Here are a couple~%of test data lines~%")) =>  NIL

if os.path.exists(filename):
    f = open(filename, 'w')
    print >> f, "Here are a couple of test data lines"

I think that the latter is easier to read and I don't have to worry 
about f.close(), but that is just me.  I don't know what with-open-file 
does if filename actually can't be opened but you haven't specified what 
to do in this case with your example so I won't dig deeper.

> BTW, this is one of the typical uses for macros: When designing APIs, 
> you usually want to make sure that certain protocols are followed. For 
> the typical uses of your library you can provide high-level macros that 
> hide the details of your protocol.
I tend to use a model in this case.  For example if I want to always 
retrieve a writable stream even if a file isn't openable I just supply a 
model function or method.

model.safeOpen(filename)
"""filename -> return a writable file if the file can be opened or a 
StringIO buffer object otherwise"""

In this case what happens is explicit as it would be with a macro.  Note 
that I could have overloaded the built-in "open" function but I don't 
really feel comfortable doing that.  So far, I haven't been supplied an 
urgent need to use macros.

> Here is an arbitrary example that I have just picked from the 
> documentation for a Common Lisp library I have never used before:
> 
> (with-transaction
>  (insert-record :into [emp]
>               :attributes '(x y z)
>                :values '(a b c))
>  (update-records [emp]
>     :attributes [dept]
>     :values 50
>     :where [= [dept] 40])
>  (delete-records :from [emp]
>     :where [> [salary] 300000]))
> 
> (see 
> http://www.lispworks.com/reference/lw43/LWRM/html/lwref-460.htm#pgfId-889797 
> )
> 
> What do you think the with-transaction macro does? Do you need any more 
> information than that to understand the code?
> 
Yep.  Where's the database?  I have to look at the specification you 
provided to realize that it is provided by *default-database*.  I also 
am not aware from this macro whether the transaction is actually 
committed or not and whether it rolls back if anything goes wrong.  I 
can *assume* this at my own peril but I, of course, had to look at the 
documentation to be sure.  Now this might be lisp-centric but I, being a 
lowly scientist, couldn't use this macro without that piece of 
knowledge.  Of course, now that I know what the macro does I am free to 
use it in the future.  And yet, if I actually want to *deal* with errors 
that occur in the macro besides just rolling back the transaction I 
still need to catch the exceptions ( or write another macro :) )

The macro's that you have supplied seem to deal with creating a standard 
API for dealing with specific exceptions.

Again, I could create an explicit database model

model.transaction(commands)
"""(commands) Execute a list of sql commands in a transaction.
The transaction is rolled back if any of the commands fail
and the correspoding failed exception is raised"""


> BTW, note that the third line of this example is badly indented. Does 
> this make reading the code more difficult?
This is a red herring.  Someone had to format this code to bake it 
suitably readable.  I could add a red-herring of my own reformatting 
this into an undreadable blob but that would be rather childish on my 
part and completely irrelevant.

> Here is why I think that Python is successful: it's because it favors 
> dynamic approaches over static approaches (wrt type system, and so on). 
> I think this is why languages like Ruby, Perl and PHP are also 
> successful. Languages like Java, C and C++ are very static, and I am 
> convinced that static approaches create more problems than they solve.
We are in agreement here.

> It's clear that Python is a very successful language, but I think this 
> fact is sometimes attributed to the wrong reasons. I don't think its 
> success is based on prettier syntax or uniformity. Neither give you an 
> objectively measurable advantage.
It all depends on your perspective.  I think that I have limited brain 
power for remembering certain operations.  A case in point, I was 
re-writing some documentation yesterday for the embedded metakit 
database.  Python uses a slice notation for list operations:

list[lo:hi] -> returns a list from index lo to index hi-1

The database had a function call select:

view.select(lo, hi) -> returns a list from index lo to index hi

While it seems minor, this caused me major grief in usage and I wish it 
had been uniform with the way python selects ranges.  Now I have two 
things to remember.  I can objectively measure the difference in this 
case.  The difference is two hours of debugging because of a lack of 
uniformity.  Now, I brought this on myself by not reading the 
documentation closely enough and missing the word "(inclusive)" so I 
can't gripe to much.  I will just say that the documentation now clearly 
shows this lack of uniformity from the standard pythonic way.  Of course 
we could talk about the "should indexed arrays start with 0 or 1?" but I 
respect that there are different desired levels of uniformity.  Mine is 
probably a little higher than yours :)

Note, that I have had similar experiences in lisp where macros that I 
expected to work a certain way, as they were based on common CLHS 
macros, didn't.  For example, you wouldn't expect (with-open-file ...) 
to behave fundamentally different from (with-open-stream ...) and would 
probably be annoyed if they did.

In case anyone made it this far, I'm not dissing lisp or trying to 
promote python.  Both languages are remarkably similar.  Macros are one 
of the constructs that make lisp lisp and indentation are one of the 
things that make python python.  Macros could be extremely useful in 
python and perhaps to someone who uses them regularly, their ommision is 
a huge wart.  Having used macros in the past, all I can say is that for 
*MY* programming style, I can't say that I miss them that much and have 
given a couple of examples of why not.

> Pascal
Brian Kelley
Whitehead institute for Biomedical Research






More information about the Python-list mailing list