6 ms·
I think most people have an irrational dislike of s-expressions, but that at the same time, s-expressions are not as good as their proponents think they are. Th
by breuleux 11y ago
I think most people have an irrational dislike of s-expressions, but that at the same time, s-expressions are not as good as their proponents think they are. There's a certain aesthetic "purity" to them that's seductive, but from a practical standpoint, it is quite reasonable to adapt syntax to common patterns. The issue with mainstream languages isn't that they do that, it's that they do it too much.
But there are syntactic schemes besides s-expressions that allow for good flexibility. For instance, in my programming language Earl Grey[1], I use this very simple sugar: `a b: c` <=> `a(b, c)`. So you can use this todo syntax if you want, and it will just work:
todo "housework":
item priority(high): "Clean the house."
item priority(medium): "Wash the dishes."
item priority(medium): "Buy more soap."
Interestingly this has one advantage over s-expressions: whereas Lisp editors usually need to specify a special highlighting/indent policy for keywords like `let`, here editors can just highlight/indent `a b: c` indiscriminately.
I also think a limited number of infix operators are worth defining in most languages, and I don't mean arithmetic operators (although I'm okay with them). I mean ubiquitous or easily repurposed operators like assignment/declaration, lambda, pairing, or commas. In my view, Lisp-like languages hold to standards of purity that are unreasonable (don't get me wrong, they are quite usable, just not optimal according to any useful metric).
[1]: http://breuleux.github.io/earl-grey/ http://breuleux.github.io/earl-grey/
- drakecoffeecake 11y agoI don't think that's much clearer than a lisp equivalent: (todo "housework" (item (priority high) "Clean the house.") (item (priority medium) "Wash the dishes.") (item (priority medium) "Buy more soap.")) Yours looks nicer, but if I wanted to operate on your todo there, it's not obvious how just by looking. With the lisp, it's obvious: There's a list of 5 elements. The last three are 'items', each of which is a list of 3 elements. The benefit of s-expressions isn't just that they're flexible in what you can write with them, but that they're easy to manipulate programmatically, and their structure is immediately obvious.
- kazinator 11y agoI would say there are too many distracting "tags" in that which may be unnecessary ("XML disease"). For instance, look how you just indicated with a tag that the elements of a list are "items". In Lisp! (todo "name" (item ...) (item ...) (item ...)). Basically the symbol todo is enough of a clue about the structure, and the pure syntax can determine most of the rest, with possibly a modicum of keywords here and there. (todo housework ;; symbols for naming! (:high "Clean the house") (:medium "Wash the dishes") (:medium "Buy more soap"))
- draegtun 11y agoI was thinking the same but using Rebol: todo [ housework high: "Clean the house" medium: "Wash the dishes" medium: "Buy more soap" ] And this is so easy to validate & use via the parse dialect that comes with Rebol. Here's a validation example: todo-rule: [ set task-name word! some [ set item-priority set-word! set item-desc string! ] ] >> parse [bad] todo-rule == false >> parse [incomplete high:] todo-rule == false >> parse [incorrect high: "item1" "erggh!"] todo-rule == false >> parse [housework high: "foo" medium: "bar" medium: "baz"] todo-rule == true
- breuleux 11y agoS-expressions are not obvious "just by looking" either. Don't forget you had to learn what they were and how they worked. You know that `(a b c)` is a list of three elements, that the first element can be extracted with the `car` function, and the rest with `cdr`, and so on. You also know that `(a b)` is shorthand for `(a . (b . nil))`, which helps a lot in knowing how to manipulate it. The fact that you are already familiar with Lisp taints your perception. Not to mention Lisps, especially the new ones, throw obviousness out the window with merry abandon. Let's look at Clojure. You think it's obvious how to operate on Clojure? Here: `(let [a 1, b 2] (+ a b))`. What is that? I've mostly coded in Scheme, I don't know what square brackets are supposed to do. Vectors? How do I manipulate them and what are they doing in my let? And what does the comma do? Oh, nothing? Then why is it there? And if the comma means nothing are you telling me that if I want to list the names that are being defined by this form, I need to extract the even-indexed elements of a vector? Sure I can do that, but don't tell me you care about ease of manipulation. Anyway, sorry for the rant ;) I think the point I really want to make here is that sure, s-expressions are quite simple, but if I can explain how s-expressions work in a minute, and how my expressions work in five minutes, this isn't really a matter of ease of manipulation, this is a matter of laziness. If a language's source-to-AST rules can be fully explained in one or two paragraphs or a small table, and can be easily remembered, then that's all that matters. I think that `a b: c` <=> `a(b, c)` is trivial enough that it does not have any meaningful impact on ease of manipulation.
- mbrock 11y agoFor reference, an S-expression based syntax that could be used for this example: (todo "housework" (item :priority high "Clean the house.") (item :priority medium "Wash the dishes.") (item :priority medium "Buy more soap")) Your syntax certainly has advantages, but one can easily prefer S-expressions in this case without appealing to vague values like "aesthetic purity," nor are there any blatantly obvious "practical" downsides to using a more fully parenthesized syntax.
- auganov 11y agoMathematica is a pretty good example of a language that introduces some syntax without compromising on homoiconicity. For example a := {1 + 2 - 10, 10, {1, 2}} is just Set[a, List[Plus[1, 2, -10], 10, List[1, 2]]]
- draegtun 11y agoRebol is another homoiconic language that has infix operators: >> 1 + 2 * 3 == 9 The Rebol interpreter see this as: >> multiply add 1 2 3 == 9 Functions in Rebol have fixed arity, both add and multiple take two arguments. You can express the above with parens if you want to make it clearer: >> multiply (add 1 2) 3 == 9 Rebols infix operators have no precedence they just work left to right (like Smalltalk). Use parens where needed! >> 1 + (2 * 3) == 7