Hacker News new | past | comments | ask | show | jobs | submit login
Ask HN: Why do people say "Lisp has no syntax"? It has infinite syntax
6 points by behnamoh 44 days ago | hide | past | favorite | 7 comments
A popular notion is that Lisp has no syntax. People also say Lisp's syntax is just the one rule: everything is a list expression whose first element is the function/operator and the rest are its args.

Following this idea, recently I decided to create my own Lisp such that everything, even `def` are simply functions that update something in the look-up env table. This seemed to work in the beginning when I was using recursive descent to write my interpreter.

Using recursive descent seemed like a suitable method to parse the expressions of this Lisp-y language: Any time we see a list of at least two elements, we treat the first as function and parse the rest of elements as args, then we apply the function on the parsed arguments (supposedly, the function exists in the env).

But this only gets us so far. What if we now want to have conditionals? Can we simply treat `cond` as a function that treats its args as conditions/consequences? Technically we could, but do you actually want to parse all if/else conditions and consequences, or would you rather stop as soon as one of the conditions turns True?

So now we have to introduce a special rule: for `cond`, we don't recursively parse all the args—instead we start parsing and evaluating conditions one by one until one of them is true. Then, and only then, do we parse the associated consequence expression.

But this means that `cond` is not a function anymore because it could be that for two different inputs, it returns the same output. For example, suppose the first condition is True, and then replace the rest of the conditions with something else. `cond` still returns the same output even though its input args have changed. So `cond` is not a function anymore!

So essentially, my understanding so far is that Lisp has list expressions, but what goes on *inside* those expressions is not necessarily following one unifying syntax rule—it actually follows many rules. And things get more complicated when we throw macros in the mix: Now we have the ability to have literally any syntax within the confines of the list expressions, infinite syntax!

And for Lisps like Common Lisp and Racket (not Clojure), you could even have *reader macros* that don't necessarily expect list expressions either. So you could even ,escape the confines of list expressions—even more syntax unlocked!

What do you think about this?

PS: To be honest, this discovery has made Lisp a bit less "special and magical" for me. Now I treat it like any other language that has many syntax rules, except that for the most part, those rules are simply wrapped and expressed in a rather uniform format (list expressions). But nothing else about Lisp's syntax seems to be special. I still like Lisp, it's just that once you actually want to do computation with a Lisp, you inevitably have to stop the (function args) syntax rule and create new one. It looks like only a pure lambda calculus language implemented in Lisp (...) notation could give us the (function args) unifying syntax.




    `cond` still returns the same output even though its input args 
    have changed. So `cond` is not a function anymore!
It sounds like you're holding the definition of "pure function" backwards: a pure function can't give a different OUTPUT for the same INPUT.

A feature like "cond" is usually implemented as a macro (since the arguments are only evaluated sometimes) but that's ultimately just a shorthand for a bunch of extra "quote" wrapping.


> But this means that `cond` is not a function anymore because it could be that > A popular notion is that Lisp has no syntax. People also say Lisp's syntax is just the one rule: everything is a list expression whose first element is the function/operator and the rest are its args.

That's wrong. Lisp has functions, special operators (like IF) and macros. Also any data-object is also valid Lisp.

Valid Lisp examples:

    3          -> number
    "string"   ->
    hello      -> variable
    (print "hello world")  -> function call
special forms

    (if (> a 10) 1 0)      -> no more than three args,
                              arg 2 & 3 are evaluated depending on first argument value

    (let ((var1 20)        -> definition of local variables
          (var2 22))
      (+ var1 var2))

    (quote hello)          -> definition of a literal object
macros

    (defun foo (arg1 arg2)       -> defun is macro, which defines global functions
      (if (> arg1 arg2)
          1
          0))
> But this means that `cond` is not a function anymore because it could be that for two different inputs, it returns the same output.

A function can return the same output for different inputs.

    (defun foo (args)
      0)
Above returns 0 for all arguments. It's a valid function.

> So essentially, my understanding so far is that Lisp has list expressions, but what goes on inside those expressions is not necessarily following one unifying syntax rule—it actually follows many rules. And things get more complicated when we throw macros in the mix: Now we have the ability to have literally any syntax within the confines of the list expressions, infinite syntax!

That's true.


How I understand it is that Lisp has syntax but lacks a grammar, the syntax is just very simple. Essentially the same as Forth and Tcl, everything is either a command or data, a result of the simple single pass left right parsing. But I am not completely sure I have things right just yet.

Edit: What I am saying is that in lisp '(' is actually a command and everything after it is an argument to that command. The syntax is command - arguments; this is what gives these languages their power, the simple syntax and lack of grammar allows us to define our own grammar. Hopefully someone will correct me if I misunderstand, I could be seeing Tcl and Forth everywhere since I have been primarily working with them lately.


In Common Lisp, '(' is a macro character that triggers a reader macro to read/parse the list it surrounds with a pair ')'.

More info: https://gist.github.com/chaitanyagupta/9324402

This one demonstrates how what's read in can be parsed - sort of a "Lisp in Lisp" version of eval: https://stevelosh.com/blog/2016/06/symbolic-computation/#s11...


Most programming languages are defined in terms of program text, Lisp is not. Lisp is defined in terms of in-memory structures and what happens if you pass these in-memory structures to a hypothetic of actually existing function "eval".

With this way of viewing things your "( is actually a command" no longer is useful/ no longer makes sense. (I wouldn't say you're completely wrong, though, just that another view of things seems simpler to me.)

You could probably implement a Lisp with a scanner and a parser but things are a lot easier if you follow the traditional read-eval route (or preferably read-macroexpand-eval).


So I am just seeing Tcl and Forth everywhere. You go a bit over my head with your explanation but you have given me enough to sort this all out myself, pointed out a few gaps in my general understanding of things. Thanks.


You‘re utterly confusing parsing and evaluation.




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

Search: