Hacker News new | past | comments | ask | show | jobs | submit login

I'm almost convinced people are pretending to like the Lisp syntax. I just don't get it.

I looked at the Hy vs Python comparison, Hy is just as (if not more) verbose as Python and harder to read and reason about.

Honest inquiry here, what is the appeal or benefit of the Lisp syntax? is it just that some people have a subjective preference for it?




Some concrete advantages that come from a simple, uniform, machine-readible syntax that your text editor itself can understand and manipulate:

- It makes editing and refactoring code faster. With a single keystroke you can do things like popping bits of code in or out of scope, deleting logical blocks of code etc. It's fast.

- It's hard to explain without trying it, but it is faster and less error prone to e.g. grab a section of code inside a function and break it out into a separate function. If your lisp is functional this is even smoother (hy is not as functional as it could be last time I checked).

- You never have to think about syntax. Python for example has different syntax for different operations and introduces new syntax relatively frequently. By contrast in a lisp the syntax for setting a variable looks the same as the syntax for looping and for everything else. It's all just function calls.

- If you have an nREPL set up (it's like the python repl but it's an API your editor can talk to) it makes it easier to run segments of code that are embedded inside other bits of code. E.g. you might have some complicated piece of maths or string manipulation in a function. You can run and try it out in isolation without executing the entire function.

- Metaprogramming. This is a bit overhyped for most programmers, but having the code as a data structure means you can add new language features from your own code, build DSLs, and have code that modifies other code more easily than in other languages. I try not to use metaprogramming and macros much, but I use a lot of things that smarter people than me have made with them.

These features are a bit hard to appreciate without trying them. Highly recommended!


> By contrast in a lisp the syntax for setting a variable looks the same as the syntax for looping and for everything else. It's all just function calls.

Not really.

Setting a variable in Lisp is not a function call. IF is also not a function call. Defining a function is also not a function call. Loop operations like DO, DOLIST, DOTIMES, ... are also not function calls. Lots of things are not function calls. Macro forms are also not function calls.


Yes, my apologies, I should have said "it all looks like function calls".


  (let ((a 10) (b 20) c)
    (declare (type (integer 0 *) a b c))
    (setf c (* a b))
    c)
Above is a LET expression, a variant of a lambda application.

It does not look like a function call. It looks like an operator list form, with LET as the operator. The next element is not a function call or similar, but a binding list with three variable definitions, two of them having an init value. Next to the binding list is a declaration form, with a type declaration for the local variables. Then a sequence of forms, a body, which is evaluated top down and the last value ist returned. There is a setf form, for setting a variable. The variable in the setf form is not evaluated, it will be set to the value of the second argument.

Neither LET, DECLARE, TYPE, INTEGER, or SETF are functions. They have different syntax and/or semantics from function calls.

Thus we have:

* special control flow

* a LET syntax which is not looking like a function call

* a lexical scope created by LET

* a type declaration with special syntax

* special evaluation rules, unlike evaluation of a function form

A Lisp user will need to learn that IF, WHEN, AND, ... and a lot of other operators are not functions....



Surely all programming languages have machine-readable syntax?


They do. Parsing and manipulating it is easier with a LISP and this means the tooling to do so is ubiquitous because anybody can do it.


I don't think (fn x y z) is all that different to fn(x, y, z). The lack of finicky operator order or other syntax footguns is nice. You're basically looking at the AST as you work. You're one fewer layer of abstraction removed from the logic you are composing.

In real world Lisp, alignment conventions are used that make even a fairly nested function readable at a glance. You'd also generally work using something like paredit, so you're kind of shuffling the S-expressions around like legos. It's not a language that you'd want to write in something like Notepad.

The most important thing about the syntax, though, is that since it's basically the AST, a Lisp macro can effectively manipulate the AST directly and on the fly. This is incredibly powerful, and would be hard to achieve in an Algolian language like Python.


Because we're so used to thinking parenthesis provides an order of evaluation precedence, the fact that 'fn' is within the parenthesis is very confusing. even (fn(x y z)) would have been better. having the function name and it's arguments just next to each other with no syntactic separation is hard to follow. it's like doing arithmetic this way: "add x y z" , is it x+y = z or x=y+z? I'm sure I can get over this hurdle though.

Thanks for suggesting paredit.


I hear you. Try to shift your thinking from statements to expressions, it will make things easier.

The placement of parentheses is not arbitrary. The parentheses enclose expressions, and the nesting of expressions builds a tree. This tree, in effect, is your program's AST. Your C program compiles to something very similar; Lisp just makes it explicit.

"Add x y z" is exactly what it sounds like, if I said those words to you. The add function is usually +, so (+ x y z) is an expression that adds x, y and z, and the whole expression evaluates to the result. You can nest expressions however you like, so, for example, (+ x (* a b)) adds x to the result of the expression (* a b), which evaluates to the result of multiplying a by b.

Values and expressions are effectively interchangeable. Any one of +, x, or y, could be replaced by an arbitrarily complex expression (yes, the function too - you could replace it with a call to a higher order function, which would return a function that is then called over x and y).

The neat thing is, that's basically[0] all the syntax Lisp has. There are no reserved keywords. Everything works as per above, and can be redefined at will. You can change the language's if and else to work any way you want. You can redefine Lisp's reader to change the syntax of the language to absolutely anything you can imagine, and doing so is quite easy, since you're effectively already working at the AST level.

And you can do all of this in a running REPL without even needing to restart it.

If you're interested, check out Common Lisp: A Gentle Introduction to Symbolic Computation by David S. Touretzky.

[0] Macros are the other piece of the puzzle, but to understand them you would need to read up on Lisp more. Once you understand Lisp macros, the advantages of Lisp's syntax become obvious.


Wow, thanks very much for the detailed reply. I'm a bit excited and interested to read up on it more. I can only imagine, it must have inspired so many enthusiast level and esoteric languages.


Code thats written in Lisp is using AST differently. It makes the process of generating machine code much easier. This in turn enables macros which is meta programming not available in non Lisp languages. However on the other hand I tried this avenue and since most modern computing is not Lisp based it severely limits its potential. I'm hoping for a Rust based Clojure or variant. Clojure has the problem its based on the java ecosystem which has severe downsides. A lisp thats based on python doesnt make much sense to me personally python isnt a good language to write other languages in. I think Zig and Rust would be the interesting choices. One attempt: https://github.com/clojure-rs/ClojureRS

See also: https://paulgraham.com/avg.html


Wouldn't it make more sense then to compile existing languages to a Lisp? From what you said, it sounds like the goal of Lisp making generation of machine code faster/easier? Or is it that forcing programmers to encode there intent into a Lisp removes guessing and optimization overhead for the compiler?


You can invent another syntax with Lisp/Scheme macros if you want. When compiled or interpreted it will be macro-expanded, and then likely transpiled to an AST and then compiled into byte- or machine code.

Take a look at Racket languages for some examples.

Lisp syntax with the parens and so on means editing is inherently structural, which makes it relatively easy to reason about and restructure the code. In Python spaces have double meanings, both as separator between tokens and as a block separator, similar to e.g. {} or () in other languages. That makes structural editing relatively hard.


Read macros, ordinary macro bodies have to follow ordinary syntax rules.


As I understand, that's pretty much exactly how WASM works. It can output either a `.wasm` binary or the same code in a `.wat` text format that looks like this:

    (func (param i64) (result i64)
      local.get 0
      i64.eqz
      if (result i64)
        i64.const 1
    else
      local.get 0
      local.get 0
      i64.const 1
      i64.sub
      call 0
      i64.mul
    end)
https://en.wikipedia.org/wiki/WebAssembly#Code_representatio...


Thanks, I had no idea this was how it transpiled.


yes, you can think of Lisp almost as an intermediate language. Lisp probably lends itself well to machine code generation but I haven't done enough assembly to really know that. its not designed for that, its just a side effect of the language primitives being very very short. you can write a basic Lisp interpreter in a few hours yourself https://norvig.com/lispy.html. Creating a decent compiled language takes a lot longer than that. Lisp only requires 5 or so primitives and it doesn't have a grammar.

it is a bit ackward for humans but machines can process it better because it has less structure. for example what I thought is that Lisp could potentially be a great choice to interop with Large Language Models with, because its potentially shorter code. Good clojure code can be 5-10x shorter than python code. With LLMs size of code matters a lot.


Compared to other languages, 'lisp syntax' is very minimal. It is just a prefix notation with parenthesis for enclosing expressions, the first item usually being a function. There are only a handful of special forms to learn, which deviate from this.

The real power of lisp IMHO lies in:

  - Repl driven, dynamic development. This is hard to explain. Its like chocolate. You have to try it. You either love it or hate it.

  - Macros. This is again enabled by the 'lisp syntax. Actually lack of it...'. 
Here is an example I recently ran into when checking out Hy

https://github.com/hylang/hy/discussions/2608#discussioncomm...

This shows how much you can abstract, hide the noise without any runtime penalties..


I find lisp horrible for procedural code but fine for functional code for some reason.


By analogy, programmers like LISP over other syntax for the same reason that creative children like LEGO over other toys. It's not that the pieces in the box are more beautiful than any other individual example of molded plastic, but because they are purpose-built to be the maximizing mold such that a box full of them gives more flexibility and potential than a box of any other shape you might choose. Lisp syntax is the way it is to create a human-machine interface with as much similarity between the two sides as possible, so the human approaches machine power when you write code, and the machine approaches human reasonability when you inspect running code.

For examples, McCarthy's original purpose was to demonstrate the effectiveness of a symbolic differentiation process he had dreamt up, so he devised the syntax and meta-circular evaluator of lisp to make it maximally obvious from the program text that the differentiation system was mathematically correct, while keeping it maximally obvious from the program model definition that it was computationally concrete. In response to new trends in the programming field, Lispers write mind-bending books like "Let over Lambda", "The Art of the Metaobject Protocol", or "Software Design for Flexibility" to show that, when your syntax and model is right, you can radically change how you solve problems not by rewriting your spec or switching languages but by just adding more lisp to the lisp you already have, which has the same simplicity as radically increasing the sculptures a child can make by just adding more lego to the lego they already have.

Lisps, on the other hand, tend to add features as just more convenient versions of things they can already do: Macrology for self-adapting code? Just lisp functions on lisp data structures corresponding to lisp functions. Actors for a concurrent execution model? Lisp functions as lisp data parameterized by higher-order lisp functions. Composable continuations for error handling? A lisp function exploring a lisp data structure of lisp data structures of lisp functions. It's turtles all the way down. Paul Graham points out that you can understand the social hype the presence or absence of a feature like operator overloading as a consequence of friction-ful syntaxes, while lispers care much less because replacing a function you don't prefer with one you do for your use case is straightforward in a friction-free syntax. When he decided to build a reddit clone for tech entrepreneurs he didn't need an outside data system just to get started, he only had to spin up a pool of threads for sessions to directly modify s-expression literals in memory, which he could save or modify by printing straight to disk and load by just reading the lisp syntax back into memory like all lisp code is, with no execution intermediary like languages such as the Pythons tend to have complicating things enough to make comparatively big services like a whole database for a private gossip forum worth the effort. The syntax doesn't make lisp first-order beautiful, it makes lisp the hacker's local maximum, which is second-order beautiful, and honestly isn't much harder to get into the habit of reading once you know it's worth it.


I find non lisp harder.

In blub lang based on c:

Fn(Val Val Val) to f(1,2,3) or

Val fn val 3 + 3

In blub lang based on lisp

(Fn val val...)


Exactly. When certain smug people come about I just humor them. Like, "oh isn't that nice", when I'm really holding my nose internally. Like who dumped a bunch of toenail clippings in your code? When I see Lisp my reaction is like when my dog makes a mess on my carpet. And macros? You get paid to write code. Is it too much to write a few more lines? Python's nice and all, but Algol, that's a rugged person's language, feels very solid. Not like this squishy Lisp. Like how many parens do I have to type?? Please.


Not sure if this is sarcasm or trolling.

If trolling, it is not very subtle. :-)




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: