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