I agree with you (contra GP) that path dependency is a fine reason to prefer infix. Continuing with what we're used to is very often the preferable choice where the costs of switching are high and the benefits unclear.
I actually think that infix notation has some advantages, especially for associative operations {e.g. if (G, +) is a group and a, b, c \in G, it's much more natural to write a + b + c than to choose between the equivalent (+ (+ a b) c) and (+ a (+ b c))}.
But undeniably S-expressions are superior from a teaching point of view as they don't hide the "true" structure.
Even though I don't dislike infix at all, it's hard to argue the reason most people prefer it isn't that they're just used to it.
> But undeniably S-expressions are superior from a teaching point of view as they don't hide the "true" structure.
That's exactly what they do though. A group G is not the same as a particular choice of presentation, and an element of g is not a particular tree of generators. Linear transformations aren't matrices, numbers aren't decimal expansions, polygons aren't lines on a chalkboard - and internalizing this is extremely important for beginning math students.
> and internalizing this is extremely important for beginning math students.
I'm not saying that you're wrong, because, well, you aren't. But does the choice of notation help here? Understanding that a matrix isn't a linear transformation but only a representation of it is something that's inherently hard and requires a certain level of mathematical maturity. At that point in a sense notation doesn't really matter any longer.
Programming is, as everyone here knows, a thinking discipline first...
But when you do formulate a thought, you want the typing, navigation and editing to be as fluent, fast and expressive as possible.
You want to extract and move sub expressions without friction. You want to jump around the code and “zoom” in and out of contexts. And of course evaluate every sub expression in isolation.
All of this stuff is just much harder or limited without something like S-expressions.
Whatever language we teach in schools should be simple and quite natural to get the hang of, and should be somewhat "industry standard" if they choose to continue with said language.
Python is a great candidate because of its reliance on indents and lack of delimeters. While "we" consider lisp's parenthesis incredibly useful, new programs find it scary. Forcing it on people does not make it any less scary.
For me lisp wouldn't be enjoyable without good tooling. Parinfer, paredit, emacs, slynk, etc. make lisp enjoyable to use and edit. Python has the benefit of - you can quickly graphically install it - pip is incredibly easy to use, sometimes to its detriment. - you can edit python sanely in any text editor (even notepad).
Perhaps a lesson in introductory lesson to hy in their compsci python classes would be nice though
Racket is probably one of the best languages to start with. Incredibly beginner friendly and complete, uniform tooling.
Really? Python relies much more on delimiters than Lisp? Python has all kinds of delimiters for subscripting, comparison, slicing, destructuring, etc. Each one of this adds a lot of syntactic overhead while teaching Python to beginners. Whether indentation makes it easy or more difficult to learn Python is debatable. Indentation definitely makes it easy to read but beginners are learning to write code.
> Python is a great candidate because of its reliance on indents and lack of delimeters. While "we" consider lisp's parenthesis incredibly useful, new programs find it scary. Forcing it on people does not make it any less scary.
Can you point to some actual data or references instead of making one unfounded claim after another? Do beginners who have not been exposed to any programming language yet really find parentheses scary? Where can I see the data?
I have first hand experience in teaching both BASIC, Python and Lisp (actually, Scheme) to school children as their first programming language. Believe it or not, in my experience they found BASIC the easiest to learn, then Lisp and then Python. For first time learners Python has a lot of pitfalls (screwing up indentation even a little bit leads to errors that confuses students).
Once that is done, it makes more sense to move on to a functional language rather than an eclectic language like Python, which is mostly a collection of features that somewhat work in practice for system administration or scientific one-page scripts.
There is no deeper understanding of CS to be gained from learning Python. To the contrary, it will ruin future programmers.
Dijkstra once said the same of Basic!
e.g. APL
Not sure if I am missing something. This is an honest question.
Sure you can put other parsers in front, which might just convert a different notation into Lisp data. The difference is then, that the primitive datastructure itself has a different syntax. In Lisp we use the syntax of the primitive data notation (-> s-expressions) to encode both data AND code.
Second of all, notwithstanding interference with reader macros, then no, it isn't completely trivial unless what you're suggesting is just pointless string substitution, which would also make e.g. 1(2 3) be equal to (1 2 3).
If what you're suggesting is having an enforced hard split between lists and function/macro invocation, that pretty much just makes everything harder to do with no perceivable gain, other than looking a bit more like C-based languages. It's a bit like enforcing that any strings that are parsed as numbers somewhere in the source code must be defined using "..." while strings that aren't must use '...' instead.
The entire point is that you can manipulate code as data structures. You can rewrite the rules of the language by returning data structures. There's no reason to make this more complex.
(paris berlin london)
is a data structure, which knows nothing about Lisp. It's just a nested list. paris(berlin london)
does not make any sense as a list of symbols. Why is paris written outside of the list?Lisp chooses to write programs as nested lists of symbols.
If you start to define a specific syntax which violates the simple syntax for nested lists, then the code is no longer read by reading a simple data expression.
AFAIK if you want a list either you use (list a b c) or you use '(a b c) which I find less regular than +(1 2) or (1 2) which is equal to list(1 2)
Often, it will be interpreted to be a function call or a macro invocation in the eval step (the E in REPL).
You can sidestep this behaviour using the ' reader macro (i.e. quote) as you mentioned yourself. Quoting disables this behaviour for the form that the reader macro applies to. Reader macros occur in the R step (i.e. before the E in REPL).
It is a list data structure. There are many examples of lists which are never interpreted as function calls in Lisp, since macros run before evaluation and can change the default behaviour, e.g. in
(defun increment (x) (+ x 1))
the (x) is not a function invocation, it's a list of named function parameters.But the specific feature of Lisp is this: code is data and data can be code. Some use the word homocionic -> the program is written in its own data syntax. S-expressions are the data syntax used by Lisp and the programs are written as s-expressions.
> (+ 2 3) means apply the + function with the 2 3 arguments
Depending on the context (a b c) can have different meanings. As a list it is a list of three items. As a top-level item it is a function call. In (let (a b c) ...) it means a list of variables.
Lisp is based on s-expressions. S-expressions is a simple syntax for data:
an symbolic expression is:
a symbol: foo, bar, 1, 2 paris, +, -, delta, bear, G1345
an empty list: NIL or ()
a non-empty list: ( s-expressionn1 ... s-expression-n)
That's basically it.Lisp has three functions to work with those: READ, PRINT and EVAL. READ reads a textual s-expression to data, PRINT takes data and prints it as an s-expression, EVAL takes data and evaluates it.
Thus the toplevel of Lisp is something like
(loop (print (eval (read))))
It reads a Lisp expression in form of an s-expression, evaluates it and prints the result.> AFAIK if you want a list either you use (list a b c) or you use '(a b c) which I find less regular than +(1 2) or (1 2) which is equal to list(1 2)
(a b c) is a list. It's an s-expression. It is valid data. It's only valid Lisp, if A is an operator.
(quote (a b c)) is also a nested list AND a valid Lisp program.
But READ reads all kinds of nested lists, not just Lisp programs. It does that, because S-expressions are a general data syntax.
Compare that with most other programming languages. Their programs are not written on top of a general data-structure notation like nested lists, nested vectors, nested tables, ...
((if (condition) ‘+ ‘*) 2 3)
If for some toy reason you wanted to sometimes add and sometimes multiply. (if (condition) '+ '*) (2 3)
and have that mean a conditional function call on the arguments 2 and 3. In fact, many traditional-syntax languages allow you to do that, e.g.: Python 3.8.9 (default, Mar 30 2022, 13:51:16)
>>> def f(x): return x+1
...
>>> def g(x): return x+2
...
>>> (f if 1 else g)(4)
5
No, the real reason you want (f x y) rather than f(x y) is so you can parse it with no lookahead.It is actually possible to extend Lisp's syntax to allow f(x y) using the hack that if there is no whitespace between a symbol and an open-paren then treat that like a function call, i.e. you can tweak the Lisp parser so that:
(list f(x y) f (x y))
will read as (list (f x y) f (x y))
and so kinda sorta do the Right Thing if you like traditional function call syntax. But that is pretty hacky and fragile.In a sequence of expressions, how would I [or the computer] know how to evaluate the following
g (a b) h (c d)
If g (a b) returns a function f and h (c d) returns a value v, should the result of that evaluation be v or f(v)?Does our intuition change if I write it as:
g (a b)
h (c d)
If the default is for the result to be v, and I do want it to be f(v), it's not obvious to me how to re-write it, whereas it's obvious if the first element in a list is a function call: (g (a b)) ; ==> f
(h (c d)) ; ==> v
vs ((g (a b)) (h (c d))) ; ==> (f (v))No, instead of teaching 2 + 2 = sum (2, 2), i will teach the GOTO statement.
http://www.lispworks.com/documentation/HyperSpec/Body/m_hand...
(handler-case (/ 1 0)
(division-by-zero (condition)
(format nil "We caught ~S." condition))
((or file-error parse-error type-error) (condition)
(format nil "We should never catch ~S" condition))
(:no-error (result)
(declare (ignore result))
"We should never get here."))
That's an example of how it can be done in CL, in Scheme you've got guard. (guard (con
((warning? con)
"We should never get this.")
((assertion-violation? con)
"We should get this."))
(/ 1 0))Do you have links to the evidence? Not trying to be snarky. Genuine question. I wish to see more research and data in this space.
https://en.wikipedia.org/wiki/M-expression
The project of defining M-expressions precisely and compiling them or at least translating them into S-expressions was neither finalized nor explicitly abandoned. It just receded into the indefinite future, and a new generation of programmers appeared who preferred internal notation to any FORTRAN-like or ALGOL-like notation that could be devised.
The reason people mostly don't like it, in fact, is because they find it more complex. A simple infix expression - say (2 + 3 - 1) / (5 - 2 + 4) becomes the comparatively more complex (/ (- (+ 2 3) 1) (+ (- 5 2) 4))) - with multiple layers of nesting to parse, operators a large distance away from what they are operating on etc. There's a reason it isn't taught in primary school, and it isn't objectively superior, just a trade off some people prefer because of other benefits they think it brings.
Of course, I'm paraphrasing this classic: https://www.youtube.com/watch?v=SxdOUGdseq4
Prefix works for variadic operators like the +, - and / in your example, without operator precedence rules to get wrong. And if one can't reliably remember how those rules differ between languages so people wrap everything in parens anyway, what exactly did you gain from rejecting prefix?
I meant in terms of the complexity of the code needed to parse them. In that respect they are clearly objectively simpler. The entire field of parser theory exists entirely because of non-sexpr syntaxes. Parsing sexprs is an elementary exercise.
Ignoring the latter part and only responding to the first: Root is infix, the left side is just implied 2 here.
These kinds of expressions tend to end with a cluster of parentheses, and it becomes hard to identify the beginning and end of each one, and making sure they're being closed at the right scope. Before someone comments "the editor will keep track of them for you", I want to answer that needing a tool to keep track of nested parenthesis might be a sign that such notation needs improvement.
This is evidence that S-expressions are objectively superior to other serializations. They are objectively simpler (this is why they were chosen in the first place -- writing a parser for sexprs is vastly easier than writing a parser for just about anything else), and the fact that humans who become accustomed to them early on generally prefer not to move away from them is evidence that other representations offer no advantages, and considerable disadvantages in the form of added complexity for no objective benefit. In other words, the main reason people don't like S-expressions is that they are indoctrinated from an early age (like grade school) into objectively inferior serializations, which trace their roots literally into antiquity.
Lisp should be taught in elementary school. Children should be taught to think not in terms of of "two times three" and "two plus three" but rather "the sum of two and three" and "the product of two and three". It would make the world a better place in the long run.