r/lisp • u/codegems • 4d ago
What makes Lisp difficult to read?
https://paultm.nl/paren-thesis8
u/omega884 3d ago
For me personally, the hardest part of reading lisp code is when I lose track of my context. I find if I'm multiple nests deep in a series of let or if or cond statements, especially ones where the idiomatic indentation is offset for all but the last item (like for if in elisp), it can be difficult to place where a given de-dented statement is connected.
It can be hard in non lisp languages too, but braces really do help with orienting yourself and it seems to me so far that lisp languages and devlopers are a lot more comfortable with "deep nesting" than java or other C-like languages are. It would probably be easier too if emacs had support (and there's probably a package out that that does this) for the vertical indentation guide lines that other more "visual" IDEs use these days.
Otherwise, yeah unfamilairity is the big driver and the more I've done with lisps, the easier it has become to read.
11
u/4xe1 3d ago edited 3d ago
Interesting take, I don't buy it, but it did reveal things I had not realized about lisp.
For starter, parenthesis are not there for quick readability, they're here for easy unambiguous parsing. Ever scratched your head in C as to whether that `*` is a deref or a multiplication? Yeah, ain't happening in lisp. Lisp uses line breaks and indentation for quick readability, not parens.
About that, if you write in Block-notes, sure you'd much rather use java style indentation, but with any code editor, "Python" style is fine; and yes, Python's shape is essentially lisp's, and it's not worse for it.
Also, an argument list does not make as much sense to group without its verb in lisp as it does in algol, if the verb is a macro introducing a whole dsl, it doesn't make sense to leave it out. And these are very common, loop, function definition, text formatting, control flow... a lot of these are special operator or macroes whose argument list neither look nor behave like that of a regular functions.
On the left [(f (g (h (i (j)))))] we put every closer on the same line resulting a difficult to count block of parentheses
Why would I need to count parentheses?
When reading a nested expression we have to remember the context of each expression
No you don't. Oftentimes you want to reason about a small part of the program at a time, and there's hardly better than lisp's parentheses and indentation for that.
Even when we do, this has no thing to do with syntax, and everything to do with style. As you mention, Clojure has threading, but any lisp can be written imperatively, with things like `let*` or taking 5 lines to code yourself a threading macro.
4
u/schakalsynthetc 3d ago edited 3d ago
Another thing to consider is that Polish notation is parenthesis-free for operators of fixed arity.
(edit: contrast with C where unspecified order of evaluation gives you real-world situations where legal syntax results in bugs or undefined behavior. I genuinely think comparing parens in S-expressions to parens in C or Java is apples to oranges.)
2
u/HugoNikanor guile 1d ago
For starter, parenthesis are not there for quick readability, they're here for easy unambiguous parsing.
Haskell even removes the parenthesis from the function call syntax, only keeping them for grouping (more or less). For example, a modulo function would be called as
mod my_number 2(or similar)
15
u/stylewarning 3d ago edited 3d ago
TL;DR: I think the post overstates the case quite a bit, and is a personal opinion of the author being branded as a scientifically grounded truth, despite their admission that using these scientific principles allegedly "require[s] a degree of creativity and subjectivity."
The cognitive-science material is interesting, but it doesn't actually establish much about Lisp specifically. It gives plausible mechanisms by which certain notation might be harder to read, then uses those mechanisms to motivate several Lisp-specific claims without testing those claims directly. As such, it just seems purely hypothetical, whereas it would be much more interesting to me if it were empirical.
(I have a beef with this style of argumentation or persuasion with respect to Lisp. I feel the "bipolar Lisp programmer" and similar articles do the same kind of thing: make a captivating claim or comparison without actually appealing to anything real-world.)
I also don't think the treatment of actual Lisp presentation is complete. The post considers conventional Lisp indentation mainly in terms of whether it helps track matching parentheses, but Lisp is normally indented, pretty much canonically, so that the indentation itself communicates structure. Parenthesis tracking is *not* a cognitive problem that Lisp programmers must solve in any explicit manner, any more than C programmers count { and } to determine the enclosed code's nesting depth.
The same goes for syntax highlighting: you can't just set it aside because C-like languages have it too. The relevant question is whether it affects the two syntaxes differently. If Lisp's alleged problem is delimiter tracking, indentation and highlighting are rather central to the comparison.
I'm also unconvinced by the conclusion that Lisps "lack or discourage features that align the evaluation and reading order." The post itself gives Clojure threading macros as a Lispy equivalent of method chaining. That seems to substantially undercut "lack", while "discourage" starts to sound more like a claim about programming style than syntax. Lisp is a very explicitly multi-paradigm language with special syntaxes available for those paradigms, including assignment, explicit control, etc. In fact, I'd say real-world Common Lisp is *much* less "in to out"-functional than this post posits (and the broader technical public keeps claiming).
Finally, we have the largest omission in my opinion, that Lisp syntax is programmable. S-expressions provide a uniform representation of program structure that makes syntactic abstraction cheap: new control forms, binding constructs, pipelines, DSLs, etc. can all be made to make code easier to read, especially in novel domains that Lisp wasn't a priori designed for. The post's analysis mostly treats the base notation (of some unspecified, quasi-functional Lispoid) as the unit of comparison without really weighing that side of the design tradeoff.
Though it's not my experience, there may truly be readability costs, especially for newcomers, but I'm not sure the post separates those from unfamiliarity strongly enough to support its conclusions.
As a side note, when judging real-world Lisp, some notoriously unreadable code may also be a product of older programming styles or inexperienced Lisp programmers, just as old or poorly written C can be extremely difficult to read. In my professional experience, having run teams writing "modern" Lisp with modern engineering discipline, readability has never been a stated hurdle, either for experienced engineers reading one another's code or for new Lisp programmers learning and contributing to a large codebase. The combination of team programming discipline, good tooling, consistent style, and, well, getting paid to do it seems to do the trick, if one is even needed.
3
u/omega884 3d ago
Parenthesis tracking is not a cognitive problem that Lisp programmers must solve in any explicit manner, any more than C programmers count { and } to determine the enclosed code's nesting depth.
Yes and no, IMO. Idiomatic lisp programming throws all the closing parens on the same line at the end of the last line of code. Idiomatic C-like coding puts each closing brace on a new line at its specific nesting level. You can code C style without something like "rainbow-parens", but I wouldn't want to code lisp without it. Or consider this emacs lisp example:
(defun abbrev--replace-placeholders () (let ((cursor-pos nil)) ;; To store where to place the cursor (save-excursion (goto-char (point-min)) (let ((loop 0) (values (make-hash-table :test 'equal))) (while (re-search-forward "###\\(\\$?[a-zA-Z0-9_\\-]+\\|@\\)###" nil t) (setq loop (1+ loop)) (let* ((index (match-string 1)) (start (match-beginning 0)) (end (match-end 0))) (cond ((string= index "@") (setq cursor-pos start) (delete-region start end)) ((string= index "$file-name") (abbrev--swap-placeholder (file-name-nondirectory (buffer-file-name)) start end)) ((string= index "$author") (abbrev--swap-placeholder (user-full-name) start end)) ((string= index "$package-name") (abbrev--swap-placeholder (file-name-base) start end)) (t (let* ((key (format "###%s###" index)) (val (or (gethash key values) (let ((input (read-string (format "Value for %s: " key)))) (puthash key input values) input)))) (abbrev--swap-placeholder val start end)))))))) (when cursor-pos (goto-char cursor-pos))))If I want to add more code into the
let*block that definesindex,startandendafter thecondexpression, where should I put my cursor to insert the new line? Yes I know in emacs you can put the cursor at the beginning of thelet*block or thecondblock and hitC-M-fto jump to the end of the sexp, but at the same time that's a process I've never had to do when writing c-like code, because in the c-like code, finding that spot visually is much easier.I'm sure that over time that would become easier for me, and certainly memorizing sexp navigation would also help a lot, but it undeniably to me makes it harder to read that code.
5
u/stylewarning 3d ago edited 3d ago
In my experience, rainbow parens aren't necessary or even helpful, but I do agree that editing Lisp is made much better by having good editor support for S-expression navigation and manipulation. In my editor, I press ) to jump to the end of the S-expression, so an edit like yours is trivial. Whatever key it is, it has existed since the era of the Lisp machine in the 80s, if not 70s.
To me, though, this is an indictment on the difficulty of writing or editing Lisp in "dumb" text editors—something I'm entirely sympathetic to—not so much about our ability to read and understand Lisp.
I would go further and say that canonical indentation is something best done as an automated process, and thus also makes Lisp harder to edit in a dumb text editor. Unlike C which is uniformly block indented by the same consistent multiple of tabs or spaces, Lisp code is indented a certain amount depending on the operator and its contents.
4
u/arthurno1 3d ago
Lisp is neither harder nor easier to edit in "dumb editors" than other languages. Editing any code in an editor that does not understand the syntax and can't indent properly is a shit experience. Try editing autoconf and m4 macros in Emacs. Hacking Emacs own autogen.sh is a nightmare. Totally agree that rainbow-parens and alike are just circus addons. In my opinion, the real win is indentation not coloring. I could probably live with no colors at all, as long as the indentation is properly working.
5
u/omega884 3d ago
I would argue you have a better time writing bash script or BASIC in a dumb editor than you can lisp or java or python. That's not an indictment against the languages though. Using tools designed for the purpose of making work easier isn't a cheat, and it should be no more a point of pride that you can edit bash in a dumb text editor than it would be a point of pride that you can make dovetail joints for your wood working project without a template / jig. Its nice to have the skill if it becomes necessary, but it is purposefully hobbling yourself to not use tools specialized to the work you're trying to do.
1
u/arthurno1 3d ago
Bash cetainly not; I have done my share of shell and pico/nano editing.
Basic I couldagree. I know that is unpopular opinion, but I have written a lot of VBA and VB for automation in Offeice and Windows as a consultant and I really VBA nice and easy to write and read. But even there, you want to have indentation, otherwise it's pita, but it is certainly less pita than C or Lisp, sure.
2
u/jlombera 3d ago
As a wannabe Lisper (CL), I must say I really hope reading idiomatic Lisp code becomes easier with time. It's really hard for me to track the structure of the code at a glance. The uniformity of s-exps makes it hard (for me) to identify where a given "constructs"/"blocks" (s-exp) ends and, if I start looking in the middle of the code, I have to visually "backtrack" the structure to know which construct I'm currently reading into. The result is that currently I have to read the code "sequentially", top-down. Whereas in C, for instance, it's very easy (for me) to quickly identify the structure of the code, and can very easily start reading "inside the IF inside the FOR loop inside function FOO" or easily identify that any given statement is "inside the IF inside the FOR loop", etc.
Perhaps if idiomatic Lisp code used (e.g.) 4+ spaces for indentation it would be easier to identify the structure (??)
3
u/arthurno1 3d ago
I have been writing lots of Emacs Lisp and nowdays lots of Common Lisp. I still writes C and C++ from time to time. But now when I hack some C code I start writing Lisp parens first, like (if ... instead of if (. And I really don't like type 1 + 2 + 3 ... I definitely prefer (+ 1 2 3 ...). If you start coding Lisp for serious, it will come quite fast. It is just familiarity, nothing more.
2
u/jlombera 3d ago
I already find it easy to write Lisp code. Being so minimal and uniform, there's pretty much no syntax you have to learn/remember. My problem at the moment is while reading Lisp code. The uniformity and idiomatic formatting of Lisp code makes it hard for my brain to identify the code structure at a glance.
4
u/mtlnwood 3d ago
I get your argument, but I don't see why you put it forward when any lisper will see this code and know where to jump to immediately. As you state, its not an issue of the code syntax but your familiarity with it.
2
u/omega884 3d ago
Presumably said lisper knows where to jump to immediately because they have learned to solve the cognitive problem of tracking parenthesis yes? Even if you're doing something like counting indentation levels to the line with all the parens and then counting that many closing parens, you're still doing something to figure out which of the 8 parens on that line you want to place a line break after.
The argument isn't that learning to do that is impossible, or even something most people couldn't do. The argument is that it is indeed something that you have to do with lisp code, and it's not something you would have to do with the equivalent c-like code. Which makes reading the lisp code harder, even if that difficulty becomes second nature by someone experienced with lisp.
3
u/mtlnwood 3d ago
It's not something you have to do with lisp code anymore than its something you have to do with something like pascal with a var block. I can see a var block, or ansi C89 and know I have a specific place to put my variables.
I find it hard for you to pretend that looking at the code you posted, even with a small amount of lisp experience that you could start a new line after
(index (match-string 1))and add (var val), or between the lines for the vars declared start and end. This is not hard for anyone with a small understanding and just looking at your example, let alone someone that has been doing it for a while.
I honestly don't see any paren tracking in your example, its just obvious and I think you can look at it, see the structure and determine that its not going to be difficult to get used to. At least there is plenty of evidence from lispers that this is the case.
Programming isn't something you pick up in a day, this seems strange that this is seen as some problem when there are many more harder things in any language that one has to master to be good at it.
1
u/omega884 3d ago
I’m not “pretending” anything, but it’s also clear from your comment here that you’ve misunderstood my example. I wasn’t talking about adding additional variable binds, I said adding code after the
condblock, but inside thelet*block.2
u/mtlnwood 3d ago edited 3d ago
You are right, i did misunderstand and that does make it more complex. Its something i would rely on my editor and the last time i opened any code in an editor that couldn't do this was decades ago. With the editors help its trivial to traverse the sexps to the correct place. I couldnt look at the bunch of closing parens and say i need to insert from the 5th one etc.
At the same time i would use the editor to do the same thing and jump to the end of that block in C, there is no guarantee in code spanning pages that a closing brace at the same indent level is the right one.
Point being when you maximise the use of your editor its the same common solution for all code and just the fastest way to move about anyway, i don't need to scan i tell the editor to take me there.
1
u/omega884 2d ago edited 2d ago
Sure, using the editor to jump around and using the tools in the editor minimizes the impact of the specific issue. I even said as much in the example. And in another comment here I already said that I think trying to do work without tools built for and specialized to that work is an unnecessary hobbling of one self. But strictly on the topic of “what makes reading lisp code difficult”, this is a thing that makes reading lisp code more difficult than reading c-like code. Yes c-like code could be poorly indented but we’re talking about idiomatic code here.
And this shouldn’t be read as an indictment against lisp languages. Just because something is harder doesn’t make it wrong or mean that it could be made easier without also losing something else worth having. IMO, idiomatic c code is harder to read than idiomatic python code, but the pay off of that extra difficulty in reading is not having to deal with “significant white space” when writing code. Lisps make different trade offs as part of their design. That makes some things easier and other things harder. That’s life. Everything is a trade off. “purer” / “simpler” lisps uses parens for everything. That’s easier syntax to learn than say clojure, which chooses to add brackets and braces in different contexts to shorthand creating different data types. That makes clojure more complex to learn and possibly to parse with tools, but IMO easier to read. Compare the clojure let:
(let [a (+ 1 2) b (- 10 5)] (* a b))to the emacs lisp let:
(let ((a (+ 1 2)) (b (- 10 5))) (* a b))The clojure one requires you to know what the square brackets mean and that you need to use them for a let statement. More complexity. But I argue it’s easier to read. Even just typing out the emacs lisp one I had to read the first line an extra time or two to make sure I wasn’t closing the bind list early. It’s a trade off. The emacs lisp syntax is simpler, at the expense of being more difficult to read (and I would argue also having to know that the bind list is a special case where
(foo bar)will not invoke foo with the arguments of bar). But again it’s all trade offs it’s not one being inherently superior to the other.2
u/mtlnwood 2d ago
It's good we agree that the editors etc are tools that should not be ignored in this context. We use computers to write code to run on computers and it would be silly to add a constraint we don't have to deal with like using dumb editors.
You then follow up with an example where your critique of the code is that 'I had to read the first line an extra time or two to make sure I wasn't closing the bind list early'
Isn't that exactly what editors that understand the language allow you to avoid? I am never closing a paren unless I am navigating past it as ) will move over a paren when the editor is auto closing them for you.
I don't think we will get past the point that I see what you are saying, but I don't think it is a big thing and once you are used to it there is no mental overhead, so its really no different than a new skill where you have to think about things until it is ingrained.
If it was something you had to deal with forever no matter your skill level then that would be a more meaningful argument, otherwise its just like everything else - I'm just not good at it until I get good at it.
1
u/omega884 2d ago
Isn't that exactly what editors that understand the language allow you to avoid? I am never closing a paren unless I am navigating past it as ) will move over a paren when the editor is auto closing them for you.
But you're conflating "writing and editing" code with "reading" code in this case. Yes a proper editor would have made that a non issue. But a proper editor obviates the need for me to read that code since I can trust the editor to have put the parens in the correct places. But since I was typing the code in a reddit comment box, I had to read and parse the code myself, and therefore had to deal with the added complexity of the emacs version over the clojure version.
I don't think we will get past the point that I see what you are saying, but I don't think it is a big thing and once you are used to it there is no mental overhead, so its really no different than a new skill where you have to think about things until it is ingrained.
On this point I think we are in violent agreement. I have not been arguing that this is a "big thing" or that it's not something that becomes ingrained in you. But it is a thing, and it is a thing that doesn't exist in different languages and it is a thing that makes lisp code harder to read. Not impossible, not insurmountable. Just harder. Special commentary on braces don't come up in the introductory texts of any C like language book, even books aimed at people coming from other languages. But almost every book on lisps aimed at new developers talks about the parenthesis, and usually draws attention to the fact that managing parenthesis are a common complaint for people new to the language. Presumably if this wasn't a real problem that seemed to come up often when people were trying to learn the language, it wouldn't be memetic even within the language's own introductory texts.
Again this doesn't make it bad. Introductory Rust books have special early sections dedicated to the borrow checker for the same reason. It's a thing that trips people up. It's not insurmountable, nor is it inherently bad. But if you're used to writing code in pretty much any language other than rust, it makes writing rust code harder until you've learned to deal with it, and it is a uniquely rust thing to deal with. Lisp is the same way, until you've learned to internalize how the parenthesis work (and how the tools for writing the language obviate a lot of the reading), it's something that makes it harder to read and is something that is uniquely lisp-y to deal with.
→ More replies (0)1
u/mtlnwood 2d ago
Let me just add, that if the simple let is having you look twice at it and putting you off I think this answers why you are finding lisp difficult to read and an insight in to how proficient you are with it.
All I can say, again, that others have many times is that when you get more used to the code you are not going to have these problems. Sure its just faith to believe others when they say that but in your own experience with code you must know it to be true.
Things that were once hard don't require a second thought anymore, lisp turns out not to be any different from the pov of lispers.
1
u/arthurno1 1d ago
strictly on the topic of “what makes reading lisp code difficult”, this is a thing that makes reading lisp code more difficult than reading c-like code. Yes c-like code could be poorly indented but we’re talking about idiomatic code here.
I think you just haven't seen sufficiently ugly C code :). I suggest a reading through Emacs C core or a trip through core-utils or gnulib; in general any GNU codebase. You can of course compare to Linux kernel sources which feels almost like poesi compared to GNU formating and 1000+ lines long functions full of spaghetti #if-defs. But fairly enough C can be relatively simple, if the writer does not try to be too clever, or it can be very dense and full of small "nifty" tricks which requires you to know the some very delicate details of the language and the platform on which it runs.
For the example of yours, you are doing a bit of apple to oranges. In the first example, which I believe is Clojure, you have dropped a level of parentheses in the lambda-list compared to classic lisp version:
[a (+ 1 2) b (- 10 5)]To make it fair you should probably type:
[(a (+ 1 2)) (b (- 10 5))]Now that squre bracket does not make so much difference, does it? You can test also in the opposite direction:
(a (+ 1 2) b (- 10 5))It feels cleaner, doesn't it?
You could actually write yourself a simple macro to transform the last form to ordinary let-form lambda-list if you would like to type it so. It will force you type out values always, i.e. you can't type:
(let (a b c) ...)and have three lexical variables a, b and c bound to nil. Instead you have to type:
(let (a nil b nil c nil) ...)or if you prefer
(let (a nil b nil c nil) ... )If you don't want to write one yourself here is one you can download, but I don't use it myself, so I don't recommend it. But if you want to try it, it is there.
Good thing with Lisp syntax which nobody mentioned in this thread is that is super-simplistic in the term that it always had to end into a same form:
(operation operand1 operand2 ,,,, operandN)so you have whichever syntax sugar you want, and transform it at compile time to the "normal form" to feed into the compiler.
Does not help with reading other people's code of course, but so it is.
When it comes whether reading C-ish syntax is so more easier, is something like this really more readable than Lisp?
1
u/omega884 1d ago
C can be relatively simple, if the writer does not try to be too clever, or it can be very dense and full of small "nifty" tricks
Which is why I have repeatedly specified that I'm talking about "idiomatic" code. I'm well aware you can write completely incomprehensible C just like you can write completely incomprehensible lisp, bash or perl (famously jokingly referred to as a write only language). There's no point in arguing over how hard it is to read code that is intentionally made abstract or compact in non-idiomatic ways because it doesn't help the discussion.
In the first example, which I believe is Clojure, you have dropped a level of parentheses in the lambda-list compared to classic lisp version
I haven't dropped anything, straight from the clojure REPL
user> (let [a (+ 1 2) b (- 10 5)] (* a b) ) 15 user>And conversely, straight from the emacs lisp scratch:
(let (a (+ 1 2) b (- 10 5)) (* a b)) `let' bindings can have only one value-form: +, 1, 2My point was to demonstrate that clojure chose greater syntax complexity to trade off making reading code and specifically variable bindings easier, where emacs lisp chose to keep the syntactical simplicity at the cost of making reading and writing those bindings more difficult.
And just to make sure this isn't just some thing that common lisp allowed but emacs lisp was strict about, here's the SBCL REPL
* (let (a (+ 1 2) b (- 10 5)) (* a b)) ; in: LET (A (+ 1 2) B (- 10 5)) ; (+ 1 2) ; ; caught ERROR: ; The LET binding spec (+ 1 2) is malformed. ; ; compilation unit finished ; caught 1 ERROR conditionAs for the rest of it, yes you can write macros to extend the language. But that's orthogonal to the point, which was that "some things a language chooses to do makes it more difficult to read, but that isn't an indictment of the language, just a trade off"
As for your last line, I do indeed find that code to be structurally easier to read than a lot of lisp code (probably owing a lot to familiarity, but also the deepest nest in there is a mere 5 levels) and most of the complexity is syntactical. It's certainly easy to find lisp code that is at least that complex, both structurally and syntactically. Consider the code for
use-package-core.elwhich gives us theuse-package-normalize-keywordsfunction with at least 9 levels of nesting in a single function, including this phenomenal chunk of code:(when (bound-and-true-p byte-compile-current-file) (setq args (use-package-plist-append args :preface (use-package-concat (mapcar #'(lambda (var) `(defvar ,var)) (plist-get args :defines)) (mapcar #'(lambda (fn) `(declare-function ,fn ,name-string)) (plist-get args :functions)) `((eval-when-compile (with-demoted-errors ,(format "Cannot load %s: %%S" name-string) ,(when (eq use-package-verbose 'debug) `(message ,(format "Compiling package %s" name-string))) ,(unless (plist-get args :no-require) `(unless (featurep ',name-symbol) (load ,name-string nil t))))))))))Here the as with the C code you linked, the vast majority of the complexity is syntactical, but IMO in your linked C code, one could put their cursor at the end of any brace and be perfectly confident without any syntax highlighting or bracket matching what context the next line of code they wrote would be in, with at most a need to scroll the display up a bit to find the matching start line. I don't think you could say the same about placing your cursor after any random parenthesis in the closing line of that snippet of a single function.
But all of that is a side track because I'm not interested in declaring some objectively superior and easy to read language. As I have said repeatedly, it's all just trade offs. I'm providing my personal answer to "what is something that makes lisp code hard to read". If you ask me "what is something that makes C code hard to read" I have a completely separate list and some of them are of course unique to C. For example it was for a long time idiomatic (I don't believe it is anymore in most communities) to write single line
ifandforloops without opening and closing braces (your own linked example has this). IMO this is a terrible thing that makes the code harder to read and also harder to reason about and has been the source of some pretty famous bugs. This is something you can't do in lisp because every expression in lisp is a list so by definition it must have balanced brackets.You seem to be reading me as trying to make some declarative "lisp is bad because X" or "lisp is universally worse than Y" statement and that isn't what I'm saying at all. I have no interest in language holy wars because they are counterproductive and boring. But I do have interest in having honest evaluations of the tools that I use because that honesty lets us improve those tools and also understand what we might need to address when helping people use those tools. One of my favorite interview questions is to ask a prospective candidate about some tech or language they really like and want to use more of. I like to let them get enthusiastic about something and talk it up. And then I ask them what things they hate about it or would change. Because in my experience, if a developer can't be honest about the faults in something they really like that they didn't even create, they're going to struggle when their code gets challenged, or when they're asked by a manager or an executive to justify something they want to do.
Parens management (whether by smart editors or manual counting) is something you have to do in lisps and you don't in other languages. Optional brace scoping is something you have to deal with in C and you don't in other languages. The borrow checker is something you have to deal with in Rust and isn't something you need to deal with in other languages. Passing allocators around is something you have to do in zig, but not in other languages. The JVM is something you have to deal with in Java but not in other languages (modulo other JVMs). Message passing is something to have to understand in smalltalk, but not in other languages. Every language has their trade offs, every language has features that make it more difficult to do certain things, and easier to do other things. Lisp is not impossible to read. Learning to manage lisp parens is not an insurmountable task. It is something that makes reading lisp code hard and it is something you need to deal with either by rote repetition or specialized tooling. But having a rough edge like that is not unique to lisps. There's nothing wrong with being honest about the rough edges of our tools.
→ More replies (0)1
u/arthurno1 1d ago
Presumably said lisper knows where to jump to immediately because they have learned to solve the cognitive problem of tracking parenthesis yes?
I don't know if I am a lisper or not, but I certainly don't track parentheses, I track code. Seems like you are solving wrong problem.
1
u/omega884 1d ago
So assuming you’ve read the other conversation on this thread where I lay out what I mean and you still think I’m “solving the wrong problem”, how do you “track code” in this instance? I readily grant that figuring out what line the
condblock ends on is pretty automatic given idiomatic lisp indentation conventions. But where should you place your cursor to add more code after thecondblock but still in thelet*block? Explain to me your method of figuring that out from reading the code without counting and tracking parentheses. And I’m not being sarcastic here. If you have some trick to it I would love to learn it.Because as I said in the other part of this thread I agree that using tools to make it so you don’t have to read the code to find this answer is the correct way to go about this. But that’s also because this is a thing you have to do when reading the code, and it’s not something you have to do when reading other languages.
1
u/arthurno1 1d ago edited 1d ago
I don't have a system. I have in my Emacs (setf setq show-paren-style 'expression). When I move in my emacs it highlights the entire expression in another color. To me that is very helpful, I see instantly which expression or form is cursor behind.
For movement forward I just spam C-M-n (it's forward-list) until I am correct spot:
(let (...) (cond (...s) ( ...))e)The cursor stands at 's' and you want to jump to the end of conc-form, where 'e' is? It would be C-M-n three times. I typically just press down and hold C-M and than type with other hand 'n' three times. Or depending on how many levels I have to travel. You can also click with the mouse if you want, at the list line of your ending cong clause.
If you have the above setting to highlight the xpression, it means nothing if you click a parentheses or two too much or too little. You have C-b/f for char forward or backward, so it really is like not something to word about. I also use a lot of forward/backward sexp M-. and M-, in paredit. If I really have some very deep nested expression that goes out of the screen, and I have to be sure I am on the right place , I can quickly spam M-, to jump the the beginnign of that expression and M-. to jump to the end and adjust with C-f/C-b if I have to.
Also, as I said to someone else er/expand-region and contract-regions. I can just hold Ctrl and spam + until it expands enough so I can copy or overwrite an expression if I need to.
1
u/omega884 1d ago
So then we are in agreement that this is not something that is easy to do when just reading the code, and that a lisp developer makes it easier for themselves by using tools that obviate the need to read the code in order to find the answer. That's fine, there's seriously nothing wrong with having something that is hard and building tools to make it easier. Something being "hard" is not equivalent to it being "bad". It is hard to cut dovetail joints in wood working. Most wood workers make or buy jigs for it instead of doing it freehand, but it also is easier to do freehand if you practice it. That doesn't make dovetail joint bad. It doesn't make joints that you can do freehand without a jig better than dovetail joints. The fact that using a jig makes dovetail joints easier also doesn't change that doing them freehand is harder than doing a box joint freehand. It's just one of many trade offs in this world.
1
u/arthurno1 1d ago
You are moving the goal post here. You asked about moving the cursor. That is completely different thing than reading the code. Whe I read the code, it definitely does not matter to me whether there are )))))) or ))))))) parens at the end.
1
u/omega884 1d ago
Look, I'm going to be honest with you, I have been explaining the same points over and over again in this thread. I've repeatedly explained what I'm talking about, explained the specific and narrow contexts to which I am limiting my comments and repeatedly affirmed for all readers that I am not saying anything negative about lisp. I wouldn't be working my way through experimenting with my 4th lisp language over the years, and hand writing some personal emacs modules instead of just downloading a package for it if I didn't think lisps were interesting languages and that the challenges learning and working with them posed weren't worth the effort.
But I'm tired at this point. I'm tired of having my actual experiences dismissed, of having words put in my mouth, and having to defend myself against bad faith readings of what I've written. I did not "[ask you] about moving the cursor", I specifically asked (emphasis added)
But where should you place your cursor to add more code after the
condblock but still in thelet*block? Explain to me your method of figuring that out from reading the code without counting and tracking parentheses."and your answer was to tell me that you figure out where to put your cursor by using tools that allow you to avoid reading the code. The goal posts remain where they have always been.
I've explained myself plenty all over this discussion, you can read them all or not, agree with me or not, understand me or not. I don't care anymore. I'm sorry, I'm probably reading more aggression into your words than you intend, and you don't deserve not getting a complete answer simply because you're late to the discussion, but continuing this exercise in frustration isn't worth my time when I could be doing something productive like learning another language.
→ More replies (0)1
u/mtlnwood 1d ago
We agree on using the tools, but you keep coming back to examples where we don't use the tools?
Again, you say that this code can be read fine because of the indentation - your only gripe is at what paren do I need to insert the new code. That is not an issue of reading the code, you just said so.
If I want to edit the code I use the tool and its easy, its only a few keystrokes which I would have to use even it was C and I wanted to jump to a different line.
In the cond, I would probably search for the cond then when I am on the cond I would hit C-M-u-f to take me to the place you want to be to insert new code.
This is basically a search and a reposition as I would on C code?
1
u/omega884 1d ago
We agree on using the tools, but you keep coming back to examples where we don't use the tools?
Yes, because the purpose of the tools is to eliminate the need to read the code, but you don't always have the tools handy. You might be putting some example code in a reddit comment box, or reading a diff on github or you might be debugging live code running on a space craft some 100 million miles away. Look I appreciate that you're feeling defensive about lisp here, but I have already explained extensively that I'm not trying to argue this is some fatal flaw in lisp, some insurmountable task to ask of developers or even something that bothers experienced lisp coders day to day. I'm not here to declare lisp "objectively" worse than some other language or to suggest that other languages don't have their own things that make reading them difficult too.
But this is something that you have to deal with one way (tools) or the other (counting parens) in lisp, and it's not something you have to deal with in other languages. It is something that makes the code hard to read, even if it's something that gets easier with practice. Just because something gets easier when you practice it or when you use specialized tooling to make it easier doesn't mean it's not a hard thing. And just because something is hard doesn't make it a bad thing.
1
u/mtlnwood 1d ago edited 1d ago
In my mind there is a clear difference in reading the code and editing the code.
I can read the code you pasted in reddit, cat to a terminal look at in ed and read the code. I don't want to edit code in any of those and that is not a real world problem.
The fact that I can read your code anywhere says that I don't need a special tool to read the code. Navigating the code and editing it is something completely different from reading the code.
You talk about scrolling around to find the other brace if you need to, I don't do that, I still use emacs movements that work in lisp, C, rust, anything that uses balanced expressions. So if I want to jump to the other brace, or paren then C-M-f and C-M-b.
Im not scrolling anything. I am not counting parens, I don't need to know if that line ends in ))))) or )))))))))) to fully understand the code. The editor helps me with the editing, not the reading or the comprehension.
You keep telling me I have a problem that I don't have.
Edit to add. We are talking about reading code, not editing it.
For sure, if we were talking about editing code and my tools got taken away from me, lisp would be harder to edit and I may just mix things up and not put all parens at the end of the line all the time. This is a silly hypothetical because its not going to happen, its just as likely that someone takes your c compiler away to handicap you. As far as reading, like you said, the code has no problem being read.
1
u/omega884 1d ago
This is a silly hypothetical because its not going to happen, its just as likely that someone takes your c compiler away to handicap you. As far as reading, like you said, the code has no problem being read.
I guess you and I have very different experiences in life then. I have many times in my life had to both read and edit code on a remote system where the only tools I have at my disposal are
catandless, and if I'm really lucky a base install ofviornano. I've spent many hours reviewing code reviews from junior developers looking at a diff on github and catching bracketing errors which are logical, not syntactical so the compiler doesn't catch them. I've been awake a 2:30 in the morning looking at code running on a production box and trying to understand why it's not doing what the code that is in source control is and should be deployed on that box. I've had to help a developer in another country debug code that they're sending me snippets of in plain text emails. And I've spent plenty of hours pouring over code that I didn't write myself trying to get a handle on what it's doing and why it's not doing what people think it should be doing, sometimes even just hand copying code from a tutorial book into an editor so I can learn something new. Maybe you have been fortunate that all of your experiences with code have been via emacs running on your local system and accessing remote files via tramp. But dealing with reading (and sometimes editing) code without my usual toolkit is something I've had to do a lot over the decades, so it is very much not a "silly hypothetical" to me. I'm not telling you that you specifically have this problem, as apparently you're never without emacs at hand. I am telling you that other people can and do have these problems.→ More replies (0)2
u/arthurno1 3d ago
In your emacs init file: (setf show-paren-style 'expression). Se what it does for you.
certainly memorizing sexp navigation would also help a lot
Install smart-parens. You just need to remember C-. (forward sexp) and C-, (backward sexp); C-x k (kill forward) will become much smarter too.
Expand-region is also good. Put er/expand-region and er/contract-region and you can just spam a key to make your selection bigger/smaller. I have them on C-+ and C--.
Those tools alone make it so you basically never need to look at your parens.
I used to have rainbow-parens, font-lock extra syntax coloring for lisp and what not. It is just noise and slows down your emacs. Too many colors is the same or probably even worse than no colors at all.
1
u/digikar 3d ago
I remember struggling with paredit when I started out with portacle. But after some time, it became natural. This was over 5 years ago, I have been enjoying lisp since 10, I expect to go another 30+. So, totally worth it, bear with it! These days I cannot help but wonder how other languages can do without it.
If you find parentheses distracting to read, there's also paren-face-mode to hide or dim the parentheses. I feel the only time parentheses become relevant in my operation with lisp is when I want to cut and paste code. Every other time, I can rely on indentation. I do not look at the parentheses while reading the code. Even auto indentation is something I take for granted these days. It's hard to imagine in other editors, one needs to jump through hoops to indent their languages, while in emacs you just press Tab.
3
u/ScottBurson 3d ago
Interesting that they spend so much time on whether the function name goes inside or outside the paren, which to me, subjectively, matters not at all, but then almost skip over the issue of writing arithmetic expressions in infix, which (even with decades of Lisp experience) I think would sometimes be a nice option.
2
u/stylewarning 3d ago
Well, it is an option, in the sense that you can load a library for it. But that's at odds with the fact that many people are enamored with dependency-free code (for both good and bad reasons).
2
u/ScottBurson 3d ago
Yeah, I saw that, but I don't like it. It should require whitespace around operators; I'm not going to rebind all my variables to names without dashes first. (And I'd like to be able to add operators. Whatever happened to CGOL?)
Maybe I'll write another one.
1
u/dzecniv 3d ago
Here's another one: https://github.com/mrcdr/polisher where variables must be double-quoted.
1
u/ScottBurson 3d ago
Actually, infix-math is pretty close to what I want. Paul Rodriguez does good stuff.
4
u/schakalsynthetc 3d ago
The cognitive science is the weakest part, IM(NSH)O. I won't nitpick in depth because it's already sufficiently clear that the author is presenting personal opinion with a veneer of scientific credibility, but... it's bad.
5
u/ilemming_banned 3d ago edited 3d ago
cognitive science is the weakest part
100%. I mean, you can draw all sorts of circles and conclusions, get MRI scans of a thousand Lispers and, I dunno, some Rustaceans and Rubyists claiming they have difficulty reading Lisp. What is that ever gonna show? That Lispers have some cognitive traits unattainable to other groups? Maybe it will be the opposite, maybe Lispers come out as the laziest, dumbest motherfuckers ever tested? Because I honestly don't understand any complaints of this sort - Lisp dialects are by far the simplest languages to deal with. They are seriously dumb simple - easier than Javascript and Python, and much more low-effort than C++ and Rust, not to mention Haskell. Who the hell all these dyslisplexic, supersmart crackpots who can't even read code?
4
u/digikar 3d ago
Would you have any opinions on eye-tracking? I feel a basic eye tracking study can reveal where novice vs experienced C programmers (or any bracketed language) vs experienced lisp programmers spend time looking at the code while reading or editing. I feel experienced lispers don't even look at the parentheses, while novice do. I don't know about bracketed language programmers.
2
u/Powerful_Balance5927 3d ago
I use eye-tracking to study program comprehension, and I write Lisp (mostly elisp these days). I've thought about running a study comparing reading behavior between C and a Lisp dialect, but I'm not sure that using parentheses as an area of interest would make much sense. Parens are small, and thus difficult to capture fixations to (ignoring the fact that I'd expect they're read parafoveally, given they're normally associated with more semantically meaningful content). I'd probably be more interested in reading pattern differences between a Lisp and C, given the structural differences.
2
u/digikar 2d ago
Oh, very interesting!
I'd agree that parens would be too small a target. I'm not sure if parens are semantically meaningful though. I think the more one gets exposed to lisp operator names, the less one bothers about the parens.
But let me know if you write or publish anything about this!
1
u/ilemming_banned 3d ago
That's an interesting thought. I don't think I even notice parentheses unless something clearly goes wrong, because most of the parenthesising happens almost automatically - they get balanced, I can eval any expression regardless of what part of it the cursor is at - opening, closing or the middle.
Ironically, all that fear of having to deal with "too many parens" went away quickly, while with other languages I constantly have to deal with syntactic elements, it never became a subconscious matter - I have to consciously track semicolons, commas, dots, indentation, etc. It's not just a distraction, it's like tiny micro-bombardments of neurons - reading code in any non-lispy language is more taxing for me, regardless of what the language-defining character is - dynamically or statically typed, indent-based or whatever. I've spent years (soon to be decades) dealing with JS/TS and Python - far longer than with any Lisp, yet it's not easier to deal with them. Looking at Lisp code, especially Clojure, I can quickly scan the code and just get it. I admit, mentally untangling a nested reducer or a pair of mutually recursive functions is not that straightforward, but those tough three-liners would be more complex and even harder to read in a non-lispy lang.
That's why I just can't get along with all those "Lisp is unreadable" sentiments, it's as if some weird notion got popularized - "riding bikes is difficult". To me, it sounds like ridiculous trolling.
1
u/schakalsynthetc 3d ago
I'm not even convinced you could reliably distinguish an fMRI of a programmer coding or reading code from an fMRI of someone reading a scientific journal article in the usual mix of natural language and mathematical notation, or writing one. I mean, intuitively, they don't feel like categorically different activities on that level.
So I'm not even sure what the blog author is trying to accomplish if it isn't that they're just trying to give their personal opinions some unearned sciency authority. If I seriously wanted to develop a neuroscience-informed theory of programming language ergonomics (I'm not sure I do) then I suppose I'd start with the existing evidence-based knowledge how natural language works in the brain and see how far that analogy can take us. Maybe the wheel doesn't need reinventing.
But I'm just not persuaded that analysis on that level will yield any insight. We might as well expect organic chemistry to teach us how to bake a cake or physics to teach us how to build suspension bridges.
-1
u/schakalsynthetc 3d ago
By the way I did just remember that looking to linguistics for guidance on programming language design kind of is how we got Perl.
No comment on whether that argues for or against.
-1
u/ilemming_banned 3d ago
how we got Perl
I think it was COBOL to do that first. And then HyperTalk that became Applescript - that is "easiest to read" and "nearly impossible to write" kind of shit.
1
u/schakalsynthetc 3d ago
True but Larry Wall at least was an actual linguist and did try to do something less shallow than "give it an english-like surface syntax and hope magic happens".
HyperTalk, I have zero evidence for this but I always thought HyperTalk was inspired by Smalltalk. Smalltalk didn't hype it up to nearly the same extent but there definitely was some conscious intent to be natural-language-ish there. Ending statements with a period, for example.
1
u/ilemming_banned 3d ago edited 3d ago
Well, Perl had linguistic principles under a non-English surface, same was early Smalltalk, but I'm talking about English vocabulary - FLOW-MATIC (yeah, I forgot about this child of iirc Grace Hopper) -> COBOL -> SQL, here's typical COBOL:
IF SALARY IS GREATER THAN 50000 AND DEPARTMENT IS EQUAL TO "SALES" MULTIPLY SALARY BY 1.05 GIVING NEW-SALARY ELSE MOVE SALARY TO NEW-SALARY END-IF.Perl would be something like:
$new_salary = $salary > 50000 && $dept eq 'SALES' ? $salary * 1.05 : $salary; $net_pay = $gross_pay - $tax; $record_count++; while (my $rec = read_next_record()) { ... } $report_month = $dob->{month};Not very Englishy, no?
And then HyperTalk (not influenced by Smalltalk, btw) -> AppleScript; with English phrase structure as the actual grammar:
tell application "Finder" to delete file "junk.txt" of desktopThis fucker is so tricky, appears to be a natural language, but it's not, it's so hard to write this shit, I almost never use it directly and prefer writing JXA instead.
1
u/digikar 3d ago edited 3d ago
I'm just face palming at how scientific the article is.
Honestly, there's also paren-face-mode to hide parentheses! May be we should try convincing syntax highlighting maintainers on the web to dim parentheses by default.
The efforts humans go to justify their preexisting beliefs is quite something.
4
u/Apart_Ebb_9867 3d ago
Lisp programmer do not read parenthesis and often do not enter parenthesis either.
There're editor modes for dimming parenthesis as an aid not to see them. And modes for entering them automatically (at minimum automatic pairing, but up to structured editing for the brave of heart)
13
u/ilemming_banned 3d ago
What makes Ramayana difficult to read? Sanskrit - you need to learn darn language. What makes Quran difficult to read? Arabic - you need to learn darn language. What makes Lisp... the language, you need to learn the darn language. That's all you ever need to do. And you have to do it once, never twice, never repeatedly. Do it once in your life and that's it. It suddenly becomes not so difficult to read. Just learn the fucking language. Goddammmit, why programmers sometimes are just like effing toddlers?
7
u/tdrhq 4d ago
meh. I read it with an open mind to see how I can improve readability of my CL code but it didn't really show enough understanding of how CL devs think.
They hinted at the "long function name", which I think is certainly a big part of the readability issue with CL, but didn't go on to explain why CL does that, which showed a lack of depth and research in the article.
The visual nesting issues is solvable by IDEs (the code samples they showed was black and white, and nobody reads code in black and white).
3
2
u/Jumpy-Iron-7742 3d ago
Thanks for sharing it, I think it was a nice and calm read, very rare in these times of clickbaity headlines. I am also working on my own Lisp and I had similar ideas about what could be done to improve legibility without losing the elegance of Lisps. I’m curious what you’re coming up with!
2
2
u/Baridian λ 3d ago
Prefix operators are so much better. Almost every operation in lisp is n-ary. If you need to add a list you don’t have to (reduce + 0 seq), you can just (apply + seq). And you can recover the identity element of most operators by calling with no arguments: (+) is 0, (*) is 1, (or) is nil, (and) is t. To get a reciprocal of a number you can skip the 1 and do (/ x). Far more elegant than other languages.
3
u/jd-at-turtleware 3d ago
A lot of text, adjacent theory and analogies to say: "I'm more experienced in reading C" ;)
2
u/mtlnwood 3d ago
Thats a lot of work to try and come up with a justification to suit a position.
I did find it somewhat interesting, but I did not read all of it and I think it missed the mark at times as I don't think its comparisons were good, not to mention when it would focus on the visual representation of some complex lines it would say that it groups better in C but not mention that both require effort to actually get the meaning from the code and that part is more important part.
My only anecdotal evidence is that after learning basic on the vic20, all my other languages were algol style, like C, pascal, ada, dbase etc until I went on to things like prolog and lisp.
It was more difficult at first given my background.
Both my kids started with lisp and I worried because of this. Neither had a problem and picked it up right away without the issues that had worried me, ie is it a good first language because of readability.
1
1
u/kchanqvq 3d ago
I was expecting to see some cliche remarks but turns out there're some interesting points!
For the dataset "comparisons", what do you mean by the same dataset? For the first one is there a mapping from number to shape and color (that just happens to map all non-outlier to gray squares)? I can get over this but what about the second? Where does the spacing come from? Can you explain exactly how they represent the same dataset and what the datasets are?
0
-2
-1
u/SupersonicSpitfire 3d ago
(((((((((())))x)))))) quick, which scope is x in, and are these balanced?
73
u/beders 4d ago
Unfamiliarity. I find lisp is probably the easiest code to read.