I thought it would be a down with Lisp article, but it was like my experience with the language, omg, how cool. From that ancient introduction to Lisp all I learned was that Lisp was as close to perfection and beauty as I would get in a programming language.
Serious question: what is it about the "beauty" of Lisp that the HN community seems to like so much? To me, how Lisp looks is its worst quality--the number of parentheses is just mind boggling. I want to understand why it is so loved.
Presumably, this is because you've looked at Lisp code, but you probably haven't understood Lisp.
Criticizing it on the basis of parenthesis would be roughly equivalent to criticizing someone talking about the beauty of mathematics on the basis of the color of the piece of chalk they're using. It's true that it's criticism about aesthetics, but it's not criticism about the aesthetics that the mathematician was talking about.
Once you've understood in what sense people mean that Lisp is beautiful, you can disagree, but this disagreement will not concern parentheses.
As an aside: I have a similarly shallow aesthetic aversion to JS syntax for the same reason. When I encounter a 12-line ragged cascade of curly brackets, square brackets and parentheses, sprinkled with semicolons, I wonder how that can be considered OK.
Aside 2: with Lisp, you edit programs structurally, meaning that their position is essentially managed for you. This is what people refer to when they mean that "the brackets disappear after a while." You're not focusing on them; they're handled by something else.
In my editor, I have the closing brackets faded nearly into the background, which reflects how concerned I am about their existence.
>When I encounter a 12-line ragged cascade of curly brackets, square brackets and parentheses, sprinkled with semicolons, I wonder how that can be considered OK.
It's considered OK because the extra syntax carries semantic weight, and the semicolons disambiguate intent for the interpreter (because while semicolons are optional in javascript, leaving them out can lead to errors.)
And as someone just beginning to play around with (Arc) Lisp, I still can appreciate both paradigms. Neither is objectively wrong.
> It's considered OK because the extra syntax carries semantic weight, and the semicolons disambiguate intent for the interpreter (because while semicolons are optional in javascript, leaving them out can lead to errors.)
Which is a very roundabout way of saying that JavaScript has a very complicated grammar. Is it necessary? No, it isn't. People use and love languages with much simpler grammars, such as FORTH (reverse Polish notation), APL (monadic and dyadic algebraic notation), and Lisp (fully parenthesized Polish notation).
> Presumably, this is because you've looked at Lisp code, but you probably haven't understood Lisp.
I'm not sure this is a great presumption. Someone can be familiar with lisps, understand the design choices involved, even deeply appretiate the beauty of the resulting language, while still prefering languages with a more flexible syntax
> This is what people refer to when they mean that "the brackets disappear after a while." You're not focusing on them; they're handled by something else.
This is exactly the problem. Lisps surrender the issue of syntax completely to the interpreter/compiler, which makes it easy for a machine to parse, but harder for a human. I personally prefer syntax to be designed for humans to read and write, because its going to be translated to something different to be executed anyway.
Now, the simplicity of the lisp syntax is of course intimately connected to the homoiconicity of the language and the extremely powerful macros, so I do understand the value of it and the tradeoffs involved. (Although I find it slightly ironic how it's venerated, given that the original lisp actually had two syntaxes, M-expressions for the human to manipulate and S-expressions for the machine to manipulate).
I personally prefer to do my "meta-programming" in the type-system and have flexible syntax to express functions in different ways relevant to the uses of the language. That can be Elm with it's simple type system and elegant operators for application (|> and <|) and composition (>> and <<) which indicate direction, or it can be Agda with it's unicode syntax and custom mixfix operators, where you can define a if-then-else as a function as "if_then_else_ : (b : Bool) -> (x : a) -> (y : a) -> a" and call it "if <b> then <x> else <y>". I find that the way these languages use operators, reduces the need for parenthesis and allows you to express what your code is doing, whether it's a pipeline of functions or some imperative-like steps being done in order. The result is a syntax that I find clearer.
I am however, well aware that a lot of people dislike Haskell's use of operators and that these languages are a minority in terms of usage indicating that my preference might not be common, but I don't necessarily presume that it's because they don't understand Haskell.
> which makes it easy for a machine to parse, but harder for a human.
That is a naive fallacy. What is harder for the machine is harder for the human.
By and large, humans rely on formatting cues, especially indentation, to parse programs. Human eyes can be fooled fooled by bad indentation:
/* C */
if (foo)
bar();
cleanup();
if (outer)
if (inner)
foo();
else
bar();
If you want to compare human versus machine parsing, then you need to write all the code samples on a single line with no breaks (if the programming language allows that). All optional whitespaces that can be written as a single space should so be written. This way, the humans are actually parsing the same tokens as the machine and not relying on cues that aren't part of the language.
> I personally prefer syntax to be designed for humans to read and write
There is no such thing in existence. People who design syntax simply use their whims, rather than any cognitive science that brings in any measure of objectivity. Those whims are shaped by what those people have used before.
The concept behind Python is actually the closest to getting it right: it recognizes that people really grok indentation rather than phrase structure, and so it codifies that indentation as the phrase structure.
> Languages are a minority in terms of usage indicating that my preference might not be common
That's another fallacy. The vast majority of all programming and other computing languages that have ever been invented are not used at all, or used by a vast minority. In that vast majority, we can find the full gamut of what has been done with syntax, semantics and everything else. Popularity and lack thereof isn't trivially driven by cosmetics.
Machine and assembly languages are just another example of input that is easy to parse for human and machine.
(Machine programs are hard to understand, but in this area we don't have an easy human to machine comparison: machines generally don't understand programs. The advantage of machine language is that it doesn't have to be understood in order to be efficiently executed.)
> Lisps surrender the issue of syntax completely to the interpreter/compiler
It doesn't. It just works differently and looks different because Lisp syntax is defined on top of s-expressions. Lisp syntax is provided by built-in syntax for function calls, a bunch of special operators (let, progn, block, catch, quote, function, flet, if, ...) and a zillion macros. Each macro provides syntax - from primitive examples to complex syntax (the LOOP syntax is an example). The syntax is user-extensible by defining macros.
The level of s-expressions is a syntax for data. This syntax can be changed by readtables and readermacros.
> I'm not sure this is a great presumption. Someone can be familiar with lisps, understand the design choices involved, even deeply appretiate the beauty of the resulting language, while still prefering languages with a more flexible syntax
Yes, you can, but re-read the original comment: the implication was "how can it be beautiful with all those parentheses?" It seems clear that two different aspects of beauty were conflated.
> …what is it about the "beauty" of Lisp that the HN community seems to like so much? … the number of parentheses is just mind boggling. I want to understand why it is so loved.
Also, I would love to hear which language(s) has a more flexible syntax than Lisp, seeing as expressiveness is one of Lisp's strong points.
> Lisps surrender the issue of syntax completely to the interpreter/compiler, which makes it easy for a machine to parse, but harder for a human.
What does "surrender the issue of syntax" mean?
In any case, citation needed for this one. "Easy" and "hard" are very strongly dependent on familiarity. This human finds them easy enough to parse.
> I find that the way these languages use operators, reduces the need for parenthesis and allows you to express what your code is doing, whether it's a pipeline of functions or some imperative-like steps being done in order.
Reducing parentheses is not really a strong selling point for someone familiar with Lisp, because Lisp programmers don't really work with parentheses like in other languages (again, they're managed).
It's not that they're liked for their own sake, but they enable many advantages (such as homoiconicity, structural editing, easy, selective evaluation of expressions at any level of nesting, and so on).
That being said, in Clojure, I often prefer to pipe functions like you mentioned when it seems like the expression would end up unnecessarily nested otherwise:
> Also, I would love to hear which language(s) has a more flexible syntax than Lisp, seeing as expressiveness is one of Lisp's strong points.
I'm thinking about ML-like languages such as Elm, Haskell and Agda (to give a spectrum of examples with varying degrees of complexity in terms of language features).
> Lisps surrender the issue of syntax completely
I think I am referring to the same thing as you are when you say that they're not used like other languages, and that they are managed.
>This human finds them easy enough to parse
Easy enough is good, but I think we can do better!
> It's not that they're liked for their own sake, but they enable many advantages.
I think this is what I was trying to get at. :)
FWIW, I like lips a lot, and consider them far better than most mainstream languages.
There aren't actually more parens in a Lisp program than a program written in a C-like syntax (which is really an algol-like syntax). They just stand out more for two reasons:
1. There is less punctuation in general, so the parens are more obvious. Instead of f(x, y, z) you write (f x y z). Without the commas, the parens stand out because that's all that is left.
2. There is only one kind of parens in Lisp whereas C-like languages use at least three: (), [], and {}, so that makes any particular kind of paren less prominent.
I believe it's purely seeing lines that end with )))))))) that leads the OP's observation ("To me, how Lisp looks is its worst quality--the number of parentheses is just mind boggling"). You don't get that in a C-like syntax. In practice you don't read each individual closing paren to understand the code so it doesn't matter. But it sure looks "scary."
1. Other languages have operators which can be written without brackets, x + y vs (+ x y).
2. Precedence rules allow ex. polynomial expressions to be written without brackets, 2 * x + y vs (+ (* 2 x) y).
3. Algol-like let-bindings or monadic-do-style variable-bindings that inject bindings into the containing block (as opposed to Lisp-style let-blocks where the new scope typically corresponds to a new block) use fewer brackets and keep nesting depth smaller.
# 2 sets of brackets
fun foo(x, y, z) {
let w = x + y;
let u = w + 2*z;
let v = w - 2*z;
u * v
}
# 13 sets of brackets
(fun foo (x y z)
(let ((w (+ x y))
(u (+ w (* 2 z)))
(v (- w (* 2 z))))
(* u v)))
> There is less punctuation in general, so the parens are more obvious. Instead of f(x, y, z) you write (f x y z). Without the commas, the parens stand out because that's all that is left.
3. Lisps optimize tail calls, leading to easier and more efficient recursion. This does tend to increase depth of code.
4. The lisp punctuation style closes all levels on a single line, where most C code guidelines close one level per line. This leads to a "thick" chunk of close parens that students of lisp find harder to read at first.
1. You write everything as a List. So there is syntax, but not exactly.
2. The data is also list, and your program is also a list.
3. Algorithm are basically List Manipulation(Stacks, Queues, Trees, Adjacency lists). So its easy to write complex algorithms in Lisp.
4. Tail call recursion. This part amplifies 3. further.
5. Functional programming features.
6. REPL. I mean like a real REPL.
7. Macros.
1 - 7 helps you to express problems and their solutions with code which represents exactly that. The boiler plate and other assisting code is largely non existent.
Another reason is if a thing has been there around for the longest, more people have thought about it. Both quality and quantity of literature of it are higher than others.
> Another reason is if a thing has been there around for the longest, more people have thought about it. Both quality and quantity of literature of it are higher than others.
Being older doesn’t necessarily mean either quantity or quality of literature will be greater. MUMPS has been around for much longer than Go, for example.
I would like to make the controversial claim that homoiconicity counts against adoption; the single representation carries little structural information and the meaning of a symbol is highly dependent on its containing context. Whereas the ALGOL-derived languages use different types of bracket or other means to indicate visually what the semantics are.
Ironically modern Javascript often replicates the bracket pileup, just with "})" instead. Python does away with it by having "nonindented newline" as an invisible semantic character that closes any number of scopes.
> the meaning of a symbol is highly dependent on its containing context
That being a problem is pretty much solved by not writing 1000 line top-level expressions with thirty nesting levels.
People who actually write Lisp are engaging their imagination for the program itself. Thus their imagination is too busy to come up with scary reasons how things could go wrong that would spook them out of continuing.
Hmm. I've been thinking for a while now that Lisp (and also FP) matches how some peoples' minds work, and doesn't match how other peoples' minds work. For those who match Lisp and/or FP, it's like a revelation, and it's very freeing. For others, not so much - it's a new way of programming, and you can do it that way, but why would you want to?
You said:
> ... the single representation carries little structural information and the meaning of a symbol is highly dependent on its containing context.
That makes me wonder if the difference is abstract vs. concrete thinking - those who by nature prefer abstract thinking will find Lisp more natural, and those who prefer concrete thinking will find it clumsy and unsettling.
Choosing the "right" programming language is not just finding the right language for the task (though it is that). It's also finding the right language that fits our minds - and our minds are not identical.
I think it can be learned - but one needs to have an open mind. If one has learned how a specific program look like in something like PASCAL and then later learns Lisp, there are a bunch of concepts which need to be adjusted...
I started with BASIC, various Assembler variants, PASCAL, UCSD PASCAL, MODULA 2, ... and then learned Lisp variants and Scheme - and also learned basics of some other languages like ObjectPascal, SAIL, Prolog, Postscript, Smalltalk, ...
I'll also add that its powerful and easily deployable macro facilities due to its homoiconicity hurts adoption as well. Macros and dsls can make for increased productivity /decreased loc for individuals and small teams, but the same features can be not so good for large teams and communities, as each program may tack on more and more macros to keep in mind before fully understanding the code being read. I find it easier to read through even a lengthy file of c code using the same old familiar primitives, ymmv.
That's not how code reading works. There are at least two levels of code reading: understanding WHAT the program does and understanding HOW it works.
Most of the time a Lisp programmer wants to read WHAT the program does and that in a very descriptive notation.
Any Lisp has a lot of macros. The base Common Lisp language is full of macros. Any defining operator - functions, macros, variables, classes, structures, methods, ... - is already a macro.
For example a structure - a record - is defined like this:
It's easy to see that it defines a structure type called SHIP with 5 slots. Each slot has a default value and named options.
A programmer will NEVER need to see what the expansion looks like. The code the macro generates is twenty times larger than the source code. What the programmer actually needs is a documentation of what effects the macro has: defining a type, defining accessors for the slot, defining a type constraint for the slots, making one slot read only, ... This is better read from the documentation of this macro operator, instead of trying to see it from reading low-level operator code implementing them.
Every programmer will be happy to read this macro form - no one wants to see the expanded code, how structures are actually defined in terms of low-level operators.
Thus MACROS increase the readability of programs a lot - independent of the team size. Really no one would want to define a structure type by manually creating all the definitions for it (a type, an allocation function, slot accessors, type predicate, compile time effects, ...).
What they can make more difficult is some maintenance tasks - where bugs appear on a meta-level where programs transform code.
I use simple loop constructs like the above all the time; it's especially useful for collecting. But elaborate loop constructs in their full glory are impossible to understand and IMHO should be banned from use on any software engineering team.
Seen pretty elaborate loop constructs in large Common Lisp code bases I was working on, and my conclusion is that the only thing that should be banned is lack of willingness to spend 30 minutes at some point learning the loop syntax. Seriously, after you write few loops on your own e.g. collecting over several hash tables in parallel, you won't have much problem anymore.
I still feel current generation of programmers has a learning phobia.
Pretty sure Dick wrote LOOP. There was an ai working paper on the subject by him iirc too. Teitelman had left mit by the time I got there, though I later got to deal with DWIM (which seems to have infected web browsers and npm etc :-( ) at PARC. one great titlemanism was the addition of ] to Interlisp.
The beauty of lisp-like languages isn't in the visual appearance of the code as text (the parenthesis and layout), but in the structure of the code as trees, the uniformity, and the homoiconicity.
There's also a historical componenent in all of this. Modern languages have adopted many features from the old Lisps that were quite unique back then. The same is true with the ML language family: between the two, ML and Lisp seeded a new generation of languages that are, on average, much more powerful than the languages in common use in the 1970s and 80s. (Smalltalk deserves a special mention, but it came later.)
I'm still fond of Lisp, but languages like Python, D, Nim (and increasingly, Rust) offer a lot of the Lisp affordances that I really valued -- in particular, easy compile-time metaprogramming. The biggest missing bits are Lisp's deeply integrated REPL-driven programming, and the use of program images -- which have many drawbacks but also some benefits.
IMO, everyone should write and maintain a medium-sized program in a modern Lisp (SBCL, CCL) at least once, just to get an appreciation for how different the programming / debugging experience is. So many well-integrated tools at your disposal, even at runtime.
- Different from what you are used to does not mean worse.
- Structural simplicity. Instead of having a huge mess of syntax to keep track of, it has only a very small number of constructs. Simplicity is beautiful.
I guess "ycombinator" (a LISP idiom/macro) as subdomain, and the fact that the site code was originally written in LISP, attracts lots of LISPers. I'm personally not a big fan of LISP, much less of the frequent hijacking of threads to degenerate into discussions about sexprs supremacy and other holier-than-though topics.
To be fair you can do that in most modern languages.
(I don't know lisp) but sometimes this approach just makes the code really difficult to understand afterwards. Does Lisp have something that makes it easier or better?
You can, but it's like picking your nose with boxing gloves on[1] - it's really clumsy. In Lisp, it's natural and easy. It's maybe the same in result, but it's really different in ease of use.
[1] Credit where due - I stole that phrase from my friend Michael Pavlinch.
One could argue that it's made "natural and easy" by making Lisp itself much more difficult to write. So, in other languages, the macro authors pay the tax, but their users do not. In Lisp, everyone pays the tax. This is, perhaps, why writing macros is far more common in Lisp than in other languages with them - if you pay the tax either way, you might as well fully utilize what it buys you. Whereas in another language, you weigh the benefit that a macro will give you, against the tax you will pay if and only if you write a macro.
Most "other" languages don't have Lisp-like macros, so I'm not sure what you are talking about here.
I don't think Lisp is much more difficult to like, on the contrary, out of all the languages I know well (C, C++, Python, Java, Common Lisp) not only do I find CL _by far_ the easiest to write but also that it puts me in a state of flow (= unparalleled mental clarity, focus and productivity) which doesn't easily happen with the others.
Credentials: I spent close to 10 years writing C++ at Google.
I certainly think that Lisp is very different to most popular programming languages today and that difference is immediately obvious. This makes it very easy for people who do not like leaving their comfort zone to dismiss Lisp simply because it "feels" too strange to what they're already familiar with.
It's not the only thing people complain about ; merely the first one. And I don't think anyone can seriously claim that Common Lisp is a simple language. Powerful pragmatic languages never are.
As far as syntax goes, the proof is in the pudding. All languages have people gripe about some part of their syntax or another, but it's clear from experience that "OMG parentheses" is exceptionally prevalent. Hand-waving it away as something that people just don't get because of lack of prior exposure is not really sound - somehow other languages don't get similar complaints (at least, not as universally, and not to the same magnitude) with first-time users. Besides, why is there a lack of exposure? Why, because everything else is different. But why is it different? Isn't the obvious conclusion that Algol syntax family is vastly more prevalent for the simple reason that people prefer it, and simple homoiconicity is not sufficiently enticing?
All arguments in favor of Lisp syntax feel like they ultimately boil down to "you're holding it wrong". And that may well be so - but if so many people are finding it so awkward to hold, isn't that prima facie evidence of ergonomic deficiency? I don't claim to understand why Algol-style is easier. Maybe the way our visual processing works is just better with more varied punctuation? That's something for psychologists and brain scientists and maybe linguists to figure out. But in the meantime, we could at least acknowledge the way things are. I really like Lisp as a collection of ideas (not just the usual ones like HOF and macros, but also stuff like e.g. symbol-based namespacing, or the sheer flexibility of CLOS). But lispers have to ask themselves why, instead of Lisp seeing wider adoption, other languages - that came literally decades later, so "upstart" would be a very polite way to describe them in this context! - become vastly more successful than Lisp by appropriating its cherry-picked features.
The complainers are non-users; it's just a meme, like picking on Cobol.
So many computing languages have come and gone over the years; the number that have been created vastly outnumber those that have ever been popular, let alone that are now popular.
Lisp is amazingly vibrant as a family. People are still excited about it and there is development work going on. That's amazing for something with such old roots.
I added two instructions to the virtual machine this morning, and used them in the compiler of a Lisp dialect to eke out a little performance gain. Wo hoo!
Of course they are not. If you put on a shoe, and it's uncomfortable, you just go get a better shoe that is. This does not negate the validity of the question: why was the first shoe uncomfortable?
Lisp always catered to people with a certain state of mind. It was never a language positioned for popular appeal, and by that I mean the masses of 9-5 "brogrammers" we have today. Looking at how many people fall in love (or not) with SICP, today, and the reasons they give validates this line of reasoning. Lisp and SICP are meant for inquisitive thinkers and hackers who are willing to go DEEP. If you can superficially dismiss Lisp (and all the geniuses that worked with and improved it over the years) in the manner that you do, then certainly, Lisp is clearly not for you. You are not an artist. You are most definitely not a hacker.
You seem to think that popularity should be the prime consideration when it comes to programming language design. This is what gave us PHP and Javascript. I dare say that the people that use Lisp today (and there are plenty of those) do so because nothing else will be as good to them. They love the language. I've known people who moved jobs and got less money in order to work with Lisp. What does that say about the language?
If we go with this analogy honestly, we have to recognize that the vast majority of the programming world is hobbling around in prescribed footwear, and a good lot of it has bits of gravel and broken glass.
> Besides, why is there a lack of exposure? Why, because everything else is different. But why is it different? Isn't the obvious conclusion that Algol syntax family is vastly more prevalent for the simple reason that people prefer it, and simple homoiconicity is not sufficiently enticing?
That is just confusing cause and effect.
> All arguments in favor of Lisp syntax feel like they ultimately boil down to "you're holding it wrong". And that may well be so - but if so many people are finding it so awkward to hold, isn't that prima facie evidence of ergonomic deficiency?
You are confusing cause and effect again, this time by implying that there is something fixed about the way people learn languages (Chomsky's "language organ"). Learning is not like the shape of your hand.
Once you learn structured editing, Lisp code can be written and changed in much fewer keystrokes than code expressed in a more complicated grammar, which is actually ergonomic.
I'll grant you that I haven't given any evidence for my claim, either. But, again, the onus is on those claiming that syntax doesn't matter to prove so, against overwhelming practical evidence (showcased by user count) that it does.
>Lisp (or Scheme, in this case) is a very simple language that gets essentially everything right.
I have to wonder if you actually know all that much about Scheme. Undelimited continuations, for example, are pretty much strictly broken. I like Scheme, but to claim it's flawless is kinda ridiculous.
The biggest benefit is that you can keep a very clear model of everything that's going on in the language (well, most things), from parsing to evaluation to application. The parenthesize represent very important things about how the language works and the regularity of the language emphasizes that it's a few basic principles replicated again and again. Also, the "beautiful" lisp variants tend to be in the scheme family. Common lisp is wart and pragmatic by comparison.
There is no difference between Scheme and Common Lisp when it comes to parentheses and beauty. Everything you mention (simplicity, regularity, few basic principles) apply just the same to Common Lisp. The differences between the two are mostly standard library related (irrelevant to matters of beauty) and lisp-1 vs lisp-2 (no clear winner in terms of beauty I would say).
In many ways it is the parsimony that is beautiful. There is a ton of syntax in lisp, but there are not as many punctuation requirements. And you get to play with the grammar and many parts of syntax yourself.
Contrast this with most other languages, where there are many things the language does that you can not do. At least, not easily.
From my short study, I would guess the uniformity and simplicity of it the syntax and the ability to extend it (thanks in part to the uniformity and simplicity of its syntax).