HACKER Q&A
📣 behnamoh

Is Lisp code _too_ verbose and hard to read?


I don't mind parentheses at all, but function/variable names in Lisp are often too verbose, which means when I'm reading Lisp code, there's not much "white space" between the lines and function calls, so my eyes get tired and I end up being confused about the logic of the code.

I experience that even on newer lisps like Clojure, maybe even more so thanks to all the extra symbols. But surprisingly, I can pick up a Rust/Swift/etc. source code and quickly understand what it does, even if I don't fully know said languages.

Example: Even a simple calculator in Scheme looks terribly confusing to me:

http://www.lambdanative.org/


  👤 kazinator Accepted Answer ✓
Beware; the calculator code isn't standard Scheme; it is written in Gambit Scheme, which underlies the LambdaNative project.

The ##flonum? is a "Primitive Procedure", see here:

  https://gambitscheme.org/latest/manual/#Primitive-Procedures
which has no place in an application program like this; it's hard to imagine why the author of the calculator didn't just use Gambit's flonum? function. (In standard Scheme, the inexact? function would be used.)

The code is using Gambit-specific functions with a fl prefix like fl+, fl/ and fl, which provide floating-point-specific arithmetic functions. Standard Scheme doesn't do that; it just has +, / and for all numbers and mixtures of numbers.

It looks like the author of the code has a naive view of floating-point; he or she seems to be under the impression that if you multiply a number by 1E10, then add 0.5, take the floor and divide by 1E10, that it will thereby truncated to 10 digits after the decimal after which there are reliably zeros, such that number->string will never produce more than 10 digits after the decimal. That would be true in a decimal-based floating-point representation, not in a binary one.

So that whole expression (number->string (fl/ (flfloor (fl+ (fl* (flo n) 1.0e10) 0.5)) 1.0e10)) is doing something dodgy that should be done in an entirely different way.


👤 lispm
The code for that calculator is generally not that bad, but it lacks clues what the symbols are. In Common Lisp there are conventions for example that +foo+ is a constant, that WITH-SOMETHING is a scoping macro, etc.

It also uses longer function names and single letter variable names. That's a strange mix.

One problem with the code though is the layout/formatting. This is not what I would expect as a Lisp user.

I would expect to be able to see more block structure from the code layout:

  (define (number->neatstring n)
    (if (not (##flonum? n))
        (number->string n)
        (let* ((s (number->string
                   (fl/ (flfloor (fl+ (fl* (flo n) 1.0e10) 0.5))
                        1.0e10)))
               (sl (string-length s))
               (b (substring s 0 1))
               (e (substring s (- sl 1) sl)))
          (string-append (if (string=? b ".") "0" "")
                         (if (string=? e ".")
                             (substring s 0 (- sl 1))
                             s)))))

If I have a function call with 2 args I don't want:

  (long-function-name long-expression-1
    long-expression-2)
because that's what macros with a body would be formatted.

I would want either

  (long-function-name
    long-expression-1
    long-expression-2)
   
or

  (long-function-name long-expression-1
                      long-expression-2)
Both tell me that both expressions are on the same level syntactically and semantically: both are function arguments.

👤 db48x
It’s denser but that’s a good thing. I think that the need for a lot of empty lines is something people tend to grow out of over time. Usually they’re used to make it easier to skip over lines that are less important, but you can learn to do that sort of thing using the subtler cues available in the structure of the lisp code.

I have seen very new programmers put three or four blank lines in a row over and over again, just so that they have the visual space to let them think about a small fraction of their code at a time without getting distracted by the code before and after. Lisp programmers tend to make their functions smaller, simpler, and purer instead, and end up with blank lines only between top–level forms such as functions.


👤 gus_massa
Are you complaining about "Scheme" or "Common Lisp"?

Scheme has longer-function-names than Common Lisp.

Also Scheme use a more functional style and skip the intermediate variables. Sometimes the name of the intermediate variables are like self documentation and make the code easier to read.


👤 kazinator
Your remarks are hard to understand, because verbosity of identifiers has no bearing on the space between lines or function calls.

Lisps are sometimes bemoaned for having terse identifiers in the language core like rplacd, pairlis, mapcar, cdr, nconc, setq, ...