How explain why Python is easier/nicer than Lisp which has a simpler grammar/syntax?

2QdxY4RzWzUUiLuE at potatochowder.com 2QdxY4RzWzUUiLuE at potatochowder.com
Fri Aug 7 13:56:00 EDT 2020


On 2020-08-07 at 10:00:25 -0600,
Akkana Peck <akkana at shallowsky.com> wrote:

> I wrote:
> > > > Trying to maintain that recursive list of unclosed lists in your
> > > > brain is fun. It stretches the brain in interesting ways.
> > > > [ ... ] But I never found Lisp code very maintainable, [ ... ]
> 
> 2QdxY4RzWzUUiLuE at potatochowder.com writes:
> > "[R]ecursive list of unclosed lists"?  Can you (whoever you are) expand
> > on that?  Yes, if you eschew helper functions and local variables, then
> > you can end up with depply nested code, but that's true in any language.
> 
> To pick a function that I know is readily available in lots
> of languages, let's look at a couple implementations of the
> Haversine function (from http://rosettacode.org/wiki/Haversine_formula).
> Comments to illustrate language differences are mine.
> 
> Here's the Common Lisp version.
> 
> (defparameter *earth-radius* 6372.8)
> (defparameter *rad-conv* (/ pi 180))
>     ;; Minor complication right off: assignment of variables or constants
>     ;; is completely different depending on whether you're
>     ;; defining something in general, like here, or only for
>     ;; a subsequent clause, like the let* functions later on.
> 
> (defun deg->rad (x)
>   (* x *rad-conv*))
>     ;; Prefix notation for arithmatic is unfamiliar to most people
>     ;; since that's not what we learn in school, so that's a first
>     ;; hurdle for readability.

My first calculator was RPN, so prefix wasn't really a hurdle.  ;-)

> (defun haversine (x)
>   (expt (sin (/ x 2)) 2))
>     ;; To grok this function you need to have four levels of
>     ;; parentheses simultaneously open in your head.
> 
> (defun dist-rad (lat1 lng1 lat2 lng2)
>   (let* ((hlat (haversine (- lat2 lat1)))
>     ;; Then there's the let vs. let* issue,
>     ;; no big deal for experienced programmers
>     ;; but not entirely easy to explain to a beginner.
>     ;; On the other hand, Python has its own confusing points on
>     ;; that issue, like when you need to specify "global".
>          (hlng (haversine (- lng2 lng1)))
>          (root (sqrt (+ hlat (* (cos lat1) (cos lat2) hlng)))))
>            ;; for that expression you need 7 levels of mental parens open
>     (* 2 *earth-radius* (asin root))))
> 
> (defun dist-deg (lat1 lng1 lat2 lng2)
>   (dist-rad (deg->rad lat1)
>             (deg->rad lng1)
>             (deg->rad lat2)
>             (deg->rad lng2)))
> 
> ;; End Lisp example
> 
> Seven levels of unclosed lists that you need to keep in your head
> in order to read and understand the program at its deepest point.

Seven?  Oh, there it is, inside the calculation of root in dist-rad.

> Here's the same thing in Python:

It's equivalent.  It's not the same.  (For example, in Lisp, the
parameters are clearly marked as such by the form defparameter, but in
Python, the parameters and the temporary variables are created with the
same syntax.  When I read the Python code, how can I tell that R is a
parameter rather than a temporary variable?  But that's not related to
the question of open parentheses.  Also, the Lisp example separates the
conversion to radians into a helper function, but the Python version
doesn't.)

> from math import radians, sin, cos, sqrt, asin
>  
> def haversine(lat1, lon1, lat2, lon2):
>     R = 6372.8  # Earth radius in kilometers
>  
>     dLat = radians(lat2 - lat1)
>     dLon = radians(lon2 - lon1)
>     lat1 = radians(lat1)
>     lat2 = radians(lat2)
>  
>     a = sin(dLat / 2)**2 + cos(lat1) * cos(lat2) * sin(dLon / 2)**2
>     c = 2 * asin(sqrt(a))
>  
>     return R * c
> 
> # end Python example
> 
> The equation is still the same equation, but it looks a lot more
> like the equation you'd see in a math textbook, or write on a
> homework assignment, modulo notational issues like ** instead of
> superscripts. The deepest level of parentheses you ever need to
> keep in your head is two, but the second level is just a very
> simple function call, sqrt(a). The indentation level (the program
> structure) never gets beyond one.
> 
> On the other hand, your brain does need to keep track of more
> intermediate variables (dLat, dLon etc.) Perhaps some people might
> find that harder.

On the other other hand, there's no confusing operator precedence rules
in Lisp.  We know the basic arithmetic operators from grade school, but
who can remember whether & or and bind more tightly than or and | (sic)?
Instead of memory, some programmers use *parentheses* to *clarify*.  ;-)

> And yes, you could write the Lisp to look more like the Python or
> vice versa. You're welcome to try that, but I predict you'll find that
> even if you use the same number of intermediate variables, Lisp will
> always require you to keep that larger mental stack of open parens.

A larger stack of *parentheses*, yes, but I don't think a larger stack.

    Python:  def f(x):
                 y = b + m * x
                 return y

    Lisp:    (defun f (x)
               (let ((y (+ b (* m x))))
                 y))

A function definition, a temporary variable, the familiar "point slope"
artithmetic, and the result of the calculation/function.  Now that I've
seen enough Lisp, the parentheses disappear into the whitespace.

> > Because of Lisp's simple syntax, text editors can nearly completely
> > relieve the programmer from the tedium of indenting and formatting, and
> > even closing parenthesis, which also highlights missing/extra/unbalanced
> > parentheses pretty quickly.
> 
> I find that emacs mostly does a pretty good job of figuring out
> indentation, closing parentheses, highlighting syntax errors and
> similar boilerplate in Python. Maybe you need a smarter text editor?

Emacs... emacs... emacs...  Oh, yeah!  Emacs!  I used to edit my own
config.h file, before the great emacs/xemacs schism (and I'm using a
more modern version to compose this email).  ;-)

> But my comment about levels of parens had nothing to do with editors.
> To understand the code, you need to build up that list/tree
> structure in your head, because that's what defines the program logic.
> 
> Again, my point isn't that Python is a better or simpler language
> than Lisp. It's that it stretches the brain in different ways, and that
> I would guess (but it's just a guess) that most beginners will find
> the Python approach more intuitive and easier to learn, since it
> emphasizes things they've done before (simple declarative statements,
> equations written out in a familiar way) rather than new and unfamiliar
> concepts (prefix notation, deeply nested parentheses, tree structures).

Aha.  I think.

In Python, different levels of logical nesting use different physical
representations.  Indentation, parentheses, operators (and operator
precedence), keywords, colons.  But in Lisp, the only tool you have is
parentheses (humans use whitespace sometimes, but the compiler ignores
it).  The differences are likely real, and perhaps moreso to beginners,
but if the nesting is too deep, in either case, refactor, and everyone
has their own definition of "too deep."

For completeness, let's not forget Greenspun's Tenth (which really isn't
about the syntax) and Lisp's loop macro (which *is* entirely all about
the syntax).  ;-)


More information about the Python-list mailing list