merits of Lisp vs Python

Steven D'Aprano steve at REMOVE.THIS.cybersource.com.au
Sat Dec 9 23:12:46 EST 2006


On Sat, 09 Dec 2006 14:57:08 -0500, Bill Atkins wrote:

> Paul Rubin <http://phr.cx@NOSPAM.invalid> writes:
> 
>> There is just not that much boilerplate in Python code, so there's
>> not so much need to hide it.
> 
> Well, of course there is.  There are always going to be patterns in
> the code you write that could be collapsed.  Language has nothing to
> do with it; Lisp without macros would still be susceptible to
> boilerplate.
> 
> Here's a concrete example:
> 
>   (let ((control-code (read-next-control-code connection)))
>     (ecase control-code
>       (+break+
>         (kill-connection connection)
>         (throw :broken-by-client))
>       (+status+
>         (send-status-summary connection))
>       ((+noop+ +keep-alive+))))
>   ;; +X+ indicates a constant

Eight lines, excluding the comment, and it doesn't handle the case where
control-code is not one of +break+, +status+, +noop+ or +keep-alive+,
although the ecase macro does. And how many lines of code is ecase?

> All of that
> boilerplate is handled by the macro.  In Python, I would need to do
> something like:
> 
>   control_code = connection.read_next_control_code()
>   if control_code == +break+:
>     connection.kill()
>     throw blah
>   else if control_code == +status+:
>     connection.send_status_summary()
>   else if control_code == +noop+ || control_code == +keep_alive+:
>   else:
>     error "CONTROL_CODE fell through conditional cascade; was not one of +BREAK+, +STATUS+, +NOOP+, +KEEP_ALIVE+"

Your Python syntax is rather wonky, but that's incidental.

Nine lines, including handling the case where control_code is none of the
four constants. Ten if you add the "pass" statement that it actually
needs. And it is completely self-contained, with no external functions or
macros to understand.

Saving one line of code, at the expense of having another block of code to
write or understand -- is that really the best example of what macros are
used for in practice? You don't really save writing any boilerplate code,
except for else clause, unless you're claiming that "if" and "elif" is
boilerplate. Fine, you claim them as boilerplate. I'm going to claim all
those unnecessary brackets as boilerplate.

Yes, I know the parser needs them. But as so many people keep telling me,
once you've been writing Lisp code for a month, you don't even notice the
brackets. That makes them unnecessary for the developer, and therefore
something the computer should handle on its own. You've already split
expressions with whitespace, why should you have to use brackets as well?
That's just boilerplate.

Okay, I'm impressed that ecase can pick up on the four constants being
tested against, and feed their names (rather than merely their values)
into an error message. Python has nothing like that, and if you only have
three or four things to test against, *and* they have names, that could be
a useful thing to do. And if I've understood correctly, that's more a
feature of Lisp's symbols than of macros.

But if you're testing against fifty different values, well, is it really
useful for your error message to list all fifty names? Or do you have
another macro ecase-with-lots-of-tests?

And how does ecase handle the more general case of testing against
calculated objects rather than named constants? Or is there yet another
macro for that?

If that's the best example of what macros can be used for, frankly I'm
unimpressed. Yes, I can see some benefit. But I don't see that the benefit
is worth the added complexity. Maybe there are more complex tasks that
macros are better suited for. 



-- 
Steven.




More information about the Python-list mailing list