3 ms·
> My point was more on the rarity of "syntax breaking" macros. Why don't you consider macros like DEFUN, DEFCLASS, COND, etc to be "syntax breaking"?
by kbp 8y ago
> My point was more on the rarity of "syntax breaking" macros.
Why don't you consider macros like DEFUN, DEFCLASS, COND, etc to be "syntax
breaking"?
- taeric 8y agoI was taking "syntax" to be "ubiquitous syntax". To that end, those don't really pose a big surprise. Cond might. But really, if I said to prepare a list of test to action pairs, it has the somewhat obvious syntax to execute the first one with a true test. Pcond is a bit crazier. But you can go a long way without ever touching that one. Defclass is powerful, though again, you can go a long way without it. And if you are in a codebase using it, it is probably a touch on the ubiquitous side. Loop is just famous for having a ridiculously powerful language inside it. Much more so than the others, which, quite frankly look like normal function calls most of the time.
- kazinator 8y agoNot speaking for grandparent, I would say that cond isn't syntax-breaking because it doesn't have to employ grammar -driven parsing to recognize and delimit the clauses and their constituent forms. The nested list structure of cond that you see is the real syntax. defun could be like this, though its ANSI CL specification is quite screwed up. The sytax is given as: defun function-name lambda-list [[declaration* | documentation]] form* where the double square brackets are described in "1.4.1.2.1 Splicing in Modified BNF Syntax" which denotes that a list of elements is to be spliced into a larger structure, and the elements may appear in any order. The description of this idiotic bullshit is convoluted, but an example section gives the gist of just how idiotic: "For example, the expression (x [[A | B* | C]] y) means that at most one A, any number of B's, and at most one C can occur in any order. It is a description of any of these: (x y) (x B A C y) (x A B B B B B C y) (x C B A B B B y) but not any of these: (x B B A A C C y) (x C B C y) Note how the B elements generated by B* can be interspersed! So in the case of defun it means that we can have variations like: (defun name (args ...) decl doc decl decl decl body...) (defun name (args ...) doc decl decl decl decl body...) (defun name (args ...) decl decl decl decl doc body...) There can be at most one docstring, and an arbitrary number of declarations, and they can be mixed in any order. Note that if we interpret A|B*|C as a simple regex notation, it wants all the B's clumped together; someone really had to work hard to concoct this counterintuitive BNF extension.
- taeric 8y agoThat all said, I can't imagine that most folks are surprised by the general shape of most defun statements. The complexities that go into how it is implemented is crazy, but it is not at all like the first time you see a LOOP invocation, is it?
- kbp 8y ago> I can't imagine that most folks are surprised by the general shape of most defun statements. The general shape of a LOOP expression is just a list of symbols and lists. A complicated LOOP expression that uses many features at once doesn't end up looking very Lispy, but a shorter one like (loop :repeat 10 :do (print 'hello)) shouldn't look at all foreign, especially if you write the keywords as keywords, which many people do. I guess when people talk about the regularity of Lisp syntax, I think they usually mean that (op ...args) applies the operator OP to the list ARGS. LOOP obeys this very well: the only LOOP syntax I can think of that breaks it is `using (hash-key k)` or with HASH-VALUE. Universally accepted macros break this rule all the time, though. By shoving everything into lists, they totally abandon trying to keep with the (op ...args) form. If you see that regularity as a great advantage of Lisp's, then it seems like you should be much more bothered by the more "mundane" macros than you should be by LOOP. (loop :for i :below 10 :do (print i)) Is less "syntax-breaking" than: (dotimes (i 10) (print i))
- kazinator 8y agoCommonly used loop syntax is replete with syntax that breaks "op args ...": when <condition> do for <var> in <list> for <var0> = <init0> then <step0> and for <var1> = <init1> then <step1> collecting <expr> into <var> The normal macros that don't have a flat argument structure, but shove everything into lists are keeping with the idea of leveraging the nesting so that the construct is "parsed on arrival" (POA?) into the macro. All that remains is to access its structure.
- kbp 8y agoSyntactically, those are things that could have been implemented as operators, but aren't, they're just LOOP keywords; lots of functions take keyword arguments (edit: even if LOOP's are a little different from &key parameters). LOOP avoids using unquoted lists for things other than calling operators. edit: Oh, right, destructuring, too! I guess that also counts. Out of curiosity, how would you feel about a macro like this (ignoring that it doesn't enforce any keyword order)? (defmacro for (var &key = (then =) while do) `(do ((,var ,= ,then)) ((not ,while)) ,do))