5 ms·
No personal attack intended, but what I always find funny: the complaints about Lisp's syntax. Lisp's syntax is regular prefix notation all over and one of the
by jcynix 4y ago
No personal attack intended, but what I always find funny: the complaints about Lisp's syntax. Lisp's syntax is regular prefix notation all over and one of the defining features of the language.
Algol languages on the other hand, and math even on paper, are a rather wild bunch: they mix infix operators like plus/minus (1+1), multiplication/division (2*2), have postfix operators like faculty (9!) and even user defined functions f(x, y) in prefix notation. That should be way more off-putting theoretically, but people just grew up with this "mess" and think that fine.
Disclaimer: besides Lisp, I program in Perl or Shell too ;-)
- waynecochran 4y agoLisp really doesn’t have syntax… it’s an abstract syntax tree which is the output of most parsers
- munch117 4y agoCL has huge mounds of syntax. Every macro or special form adds to it.
- kazinator 4y agoMacros don't add syntax; they just add semantics to syntax that already exists. You can write down the syntax of a call to a macro which has not yet been written, and Lisp will read it for you, giving you the tree. It just won't be able to expand it since the semantic definition through the macro name isn't there. A conventional language will not do that. C++11 will not correctly read the shape of a C++17 phrase structure, and just complain about it not being defined. Lisp macro can treat their arguments as a flat stream of tokens and parse them, even with LALR(1) parser generator or whatever. It's not not the typical way most macros are written. Macros leverage the ability of the invocation syntax to express pre-parsed nested structures. Even if a macro goes crazy like that, it's still self-contained; the churning toilet bowl of parsing it has whipped up is cleanly confined within the boundaries of the macro call, which can be cleanly substituted anywhere an expression can be used.
- munch117 4y agoIf you don't know the syntax of let, then you might find yourself writing (let (x 5) (* x x)) where you should have written (let ((x 5)) (* x x)) That is syntax. Let me ask you a question: Are these two expressions equivalent? (f (g x)) and (let ((y (g x))) (f y)) In the absence of macros, the answer is yes. In the presence of macros, there's no way of knowing until you've seen the definition of f: If f is a macro, they might not be. You need to know the syntax of the f macro to answer the question. CL has a core language with a simple syntax. It also has a lot of macros in its standard library.
- kazinator 4y agoEven though (let (x 5) (* x x)) is wrong, it parses. We know that it has an operator, followed by two arguments, and the first argument is made up of two things. Because it parses, I don't have to correct the author's punctuation, just their understanding of the necessary shape . The wrong shape they wrote has a coherent syntax, and we can discuss the problem in terms of differences in syntax, rather than differences between gibberish and valid syntax. This is why your question is able to be, "are these expressions equivalent" and not "are these two token sequences equivalent". I can write a TDD test case for a new macro that doesn't exist yet, which won't bring down the program. E.g. (test (my:let (x 5) (+ x x)) 10) This will catch the error that foo is not defined and report that the test case failed instead of returning the expected 42 value. The next test case in the same file can execute. You couldn't have a test for new C++ or Java syntax, in a C++ or Java file being processed by a compiler that wasn't maintained in order to recognize the syntax. The file will blow up at parse time, not allowing any of the test cases in it to proceed. > You need to know the syntax of the f macro to answer the question. But I can see what that syntax is: it's (f (g x)) right there: i.e. /\ f /\ /\ () g /\ x () g and x are clumped together, so they have a closer relationship of some kind than either one has with f, at least syntactically. From looking at the use, I don't know whether it is valid shape for f, or if so, what other shapes are valid. And I don't know the semantics at all. I'm not struggling with syntax, though, just connecting it with a meaning.
- 4y ago
- jcynix 4y agoLisp does have "syntax" but it doesn't have syntactic sugar. But yes, it's very minimalistic syntactically and the "sugar" might be what people are missing on first contact. And the dreaded parenthesis? That's a thing my editor took care of in the last 30+ years.
- lispm 4y agoEvery Lisp has syntax. But the code is actual data and there is a separate syntax for this data, called s-expressions. Source code is actually simple nested list data. For example the following snippet, which binds two variables A and B, defines a local function and then calls this local function. This is just an s-expression, which consists of symbols, numbers and lists. Even though s-expressions have a syntax with numbers, parentheses, symbols, ... - they can not only be written in a text editor, one can also directly compute with them using the typical Lisp operators for lists, numbers, strings, symbols, ... The following enters literal data, which is not executed as code. CL-USER 5 > '(let ((a (+ 1 2)) (b (* 2 3))) (flet ((add3 (a b) (+ 3 a b))) (add3 a b))) (LET ((A (+ 1 2)) (B (* 2 3))) (FLET ((ADD3 (A B) (+ 3 A B))) (ADD3 A B))) We can substitute the symbol * with the symbol expt in this s-expression - the variable * just holds the last evaluation result: CL-USER 6 > (subst 'expt '* *) (LET ((A (+ 1 2)) (B (EXPT 2 3))) (FLET ((ADD3 (A B) (+ 3 A B))) (ADD3 A B))) Then this can be evaluated: CL-USER 7 > (eval *) 14 Now, even though the program is written as a data structure, the Lisp program itself has syntax. Only function calls have the simple (operator args...) syntax. But there are also special forms and macro forms. LET is a syntactic form and expects a list of (variable value) pairs as its first sub-expression. Then follows a body of subexpressions, where the variables can be used. FLET is a another syntactic form: it expects a list of function definitions and a body. The function definition starts with a name, followed by an arglist and then by a body. In reality this is more complex, since LET and FLET allow declarations, functions allow complex argument lists, declarations, documentations, etc. So for FLET the top-level EBNF syntax would be like: flet ((function-name lambda-list [[local-declaration* | local-documentation]] local-form*)*) declaration* form* With more syntax for lambda-list, declaration, documentation and form.
- pstuart 4y agoNo offense taken. My comment wasn't meant to complain about it, just that it never clicked for me. I admit that I haven't tried hard or long enough but I've made peace with the fact that I have only so many hours/brain cells/etc and it's ok to not master all the things I think I should.
- jstimpfle 4y agowell->people[2].are(likely, to, like, this, better); compared to this ((Person->are ((array-at (Prefix->people well) 2)) .... Please fix it for me, I can't be bothered. (Yes, this is an exaggerated example but this really is the point. Also, static types. If there's anything of overarching regularity in what you type, it better be syntax or it will be hard to check and take advantage of. This is what Algol-style languages do for systems programming.)
- codemac 4y agoRealistically, people would probably make this some weird macro that is: (peopler-simplifier well people 2 are likely to like this better) This is where generally macros & helper functions in lisp actually produce a new "syntax", rather than leverage a large language provided one. This to me is actually the crisis for lisp. Any codebase requires you to learn all the various macros that do: peopler-simplifier == $1->$2[$3].$4($5, $6 $7 $8 $9);
- jstimpfle 4y agoYep. There is no reuse of common patterns. Everybody is required to reinvent their own idosyncrasies.
- reikonomusha 4y agoI program in Common Lisp professionally and this is baloney. People do use common patterns, offered by the language and open-source libraries. What is being discussed in this sub-thread is an oft-cited criticism of Lisp's "power" being too much for its own good, but rarely (if ever) a reflection of idiomatic practice.
- jstimpfle 4y agoOk, you can use libraries, of course - so shouldn't say that there is no reuse. But libraries are not the same as built-in syntax. Built-in syntax for the basic things, as in the Algol-family is a) standard so everybody uses it. b) optimized for the right mix of efficiency as well as redundancy (error detection, skimmability by humans). Talk about "oatmeal with fingerclippings mixed in" - more parens don't add useful redundancy, but keywords and delimiter tokens do. Too much power for its own good is one thing, but being just not optimized for the common thing is another. How would the following function, randomly picked from what I have open on github right now, look in "Common" Lisp? And if I ask 3 persons to do it, what is the likelyhood that they would pick more-or-less the same approach? Edit_Array buffer_batch_array_from_linked_list(Arena *arena, Batch_Edit *batch, i32 count){ Edit_Array result = {}; result.count = count; result.vals = push_array(arena, Edit, count); i32 counter = 0; for (Batch_Edit *node = batch; counter < count && node != 0; node = node->next){ result.vals[counter] = node->edit; counter += 1; } return(result); }