5 ms·
It is interesting to me that prefix notation gets brought up so much in the context of Lisp. Most operations in most languages use prefix notation (function cal
by Zambyte 2y ago
It is interesting to me that prefix notation gets brought up so much in the context of Lisp. Most operations in most languages use prefix notation (function calls, procedure calls, method calls, and most macro systems). The only thing that makes Lisp unique in this context is that it also makes arithmetic operations prefix. This is just the natural side effect of treating "primitive operators" as first class functions. It's something that other languages that treat these operators as non-primitive constructs awkwardly work around, like some syntax magic in Haskell or operator overloading in other languages.
What makes Lisp macros so intuitive is not the prefix notation, it's the homoiconicity of the language. Writing a macro is just a list operation, where the familiar map / filter / reduce applies.
- gorjusborg 2y agoI love prefix-notation for arithmetic, not just because it prevents the need for disambiguating precedence. Subjective, I know, but I think (+ 1 2 3 4) is way nicer than (1 + 2 + 3 + 4).
- Zambyte 2y agoWhen dealing with pure functions, there is also no ambiguity for f(g(1), h(i(2), 3), j(4)). In both cases, prefix notation removes ambiguity.
- johnnyjeans 2y agoFor me, it's concatenative languages. Grammar so simple that BNF is of dubious usage. 4 3 2 1 + + +
- whartung 2y agoMy problem with prefix, is simply that my mind does not think in prefix. If I have to write 1 + 2 * 5 - 3, I'm not going to "start" with *. I will type: (+ 1 2<-<-(* ^E 5) <-<-<-<-<-<-<-(- ^E 3)) It indicative of how I'm thinking about it (regardless of how the navigation is done). I just have a lot of those starts and stops when converting infix to prefix in my tiny Piglet brain. That said, as a general rule, yea, I like (+ 1 2 3 4) and its ilk. It takes a bit of exploration to read. For example, I need to transit the entire list of 1 2 3 4 to understand all that is being added, which can be more difficult with longer expressions. But that can be mitigated with formatting: (+ (this that) (func zzz) (if (< v q) 1 3)) (Function conditions are also fun to intermix into things!) I think just as a rule, I find formatting more important in general with s-expr systems than algolesque systems.
- gorjusborg 2y ago> If I have to write 1 + 2 * 5 - 3 That's a great example of an expression that can be ambiguous, but not with prefix/suffix notation. Most people that write lisp-like code use an editor that helps with s-exp editing (like paredit), so that isn't really a significant issue. In fact, I think it is faster to write s-exp code than algol-likes once you've become accustomed. I agree that formatting is very important w/ parens langs, but there are many languages that consider formatting a high concern. I would also argue that reading code is harder than writing it. Optimizations that speed up writing code is less interesting to me than ones the help reading/understanding/making assertions easier about code. Finally, infix notation is available in lisp/scheme, it's just a macro away (but seriously, don't).
- koito17 2y agoI find value in ease of "local" modifications to code. My editor being able to indent as soon as I type or paste code is a huge time saver. What's an even bigger time saver is my editor being able to perform tree operations on tree-like data. When using "curly brace languages", what I really miss is structural editing. I often deal with tree-like structures in any language, and being unable to simply cut a node, move my cursor to the start/end of a node, nest something, etc. is really inconvenient. These are operations that take me at most a second when using Emacs with smartparens. In JSX, for example, these one-second operations need me to imitate smartparens' behavior by hand then run Prettier since indenting by hand is an even bigger waste of time. Transforming e.g. <Foo> <Bar> <Baz /> </Bar> </Foo> to <Foo> <Bar /> <Baz /> </Foo> takes *seconds* even though the equivalent operation with S-expressions is a single keybind in Emacs.
- JonChesterfield 2y agoWhere this happy infix world goes wrong is precedence rules, especially where they're different between different languages. The numerical operators, with the Boolean operators, negation, comparisons, maybe implicit type conversions as well. Maybe && as separate from &. Maybe user defined operators with different precedence to the builtin ones. Maybe the large precedence table you have to keep in working memory to use the infix conventions is still simpler. Maybe even when there's a style guide saying to put lots of parentheses in as well. It starts to be difficult to fit a definition of simple to the observations though.
- psychoslave 2y agoI prefer [1,2,3,4].sum that you can get in ruby out of the box, but `1 2 3 4 sum` would look great too. Now if we want to handle compounded expression, you need either a given constant finite number of argument, or have a reserved word to mark grouping, much like `)`. A bit like stack based programming languages.
- agumonkey 2y agoThat's the usual issue, people want their language to be a glorified pocket calculator, so they can type their usual formulas as-is, while lisp rapidly grew a culture of searching for new and larger abstractions. I know some people who would die writing an imperative for loop over an external accumulator rather than (+ nums...) of (fold, +, nums). I keep trying to pin point the psychology of it. Cause even as a kid, I was more attracted toward HP calcs even though I never heard of lisp at this point, but RPL felt like infinite magic. And of course when I ran into emacs, I felt the same strange feeling.. there's something.
- fuzztester 2y agoFORTH go PROSPER and
- munificent 2y agoMethod calls are infix: target.method(argument, another) Here, the operation is `method` and the operands are `target`, `argument`, and `another. Note that the operation appears in the middle of the operands.
- Zambyte 2y agoFair point. I was considering `target.method` as the operator.
- munificent 2y agoIt depends on the language's semantics. Sometimes (as in Python), `target.method(a, b)` really is two separate operations, `target.method` (which returns a bound function) and then `<thing>(a, b)` which applies arguments to the result. Even then, it's still not a prefix operation. It's postfix.
- fuzztester 2y agoWhew. (Wipes sweat off brow). Things like this are mentioned, in some detail, in the O'Reilly book Python in a Nutshell, IIRC, in the first edition, which is what I have. Couldn't quite wrap my head around all the material in that chapter, TBH. But I still like to read about such stuff, and I do understand bits and pieces of it.
- anamax 2y agoIn Python, the "." in "target . method" is an infix operator. Note that the "(" in (a, b) is also an operator. (There is some special parsing because otherwise you could do things like args = (1, 2, b=3); target.method args.) As to whether <thing from target.method><thing from (a, b)> is postfix, I'm not sure how you get there. Yes, there's an implicit funcall at the end, but there's something similar with (+ a b) or ((. target method) (arglist a b)) and we wouldn't call them postfix.
- munificent 2y ago
- stcredzero 2y agoWhat makes Lisp macros so intuitive is not the prefix notation The culture is the ultimate "reality distortion field." Prefix notation would be seen as intuitive, if that were culturally established. We'd see something like PEMDAS as arbitrary and silly. Just look at how much content there is around PEMDAS and interpretation of math problems. Clearly, it really isn't "intuitive." We just have this enshrined in the culture. (That said, one of the biggest UX mistakes the designers of Smalltalk made, was to eschew operator precedence!)
- deleted 2y ago[deleted]
- Zambyte 2y agoMy intention wasn't to argue whether or not prefix notation is intuitive. The point I wanted to make was the intuitiveness of Lisp macros is mostly unrelated to the use of prefix notation. Homoiconicity matters a lot more for macros.
- mpweiher 2y ago> Most operations in most languages use prefix notation Yeah, and IMHO most operations in most languages suffer from being mostly prefix. Yes, LISP is not that much worse, but doubling down on the bad part doesn't exactly recommend it. ¯\_(ツ)_/¯ One of the cool things about Smalltalk is that it consistently makes everything infix.
- zajio1am 2y ago> The only thing that makes Lisp unique in this context is that it also makes arithmetic operations prefix I think that most problematic for prefix notation are not arithmetic operations, but field accessors. In C you have a->b->c->d, in Lisp it would be (d (c (b a))), which makes you to jump to the center and read it from the inside-out.
- lispm 2y agoIn Lisp one could have (-> a b c d), just like (+ (+ 2 3) 4) is (+ 2 3 4).
- Zambyte 2y agoThe direct translation of the C expression to Lisp would actually be (-> a (-> b (-> c d))), which is fine to read from left to right as well. As lispm said, this can be reduced to (-> a b c d) due to the flexibility of prefix operations. The direct translation of the Lisp expression you shared to C would be: d(c(b(a))), which just like the Lisp expression, evaluates from the middle out. Both are fine to read from left to right though.
- zajio1am 2y ago> The direct translation of the C expression to Lisp would actually be (-> a (-> b (-> c d))) Not really. -> looks like binary infix operator, but it is really an unary postfix operator (parametrized by field). Because field itself is not a first-class entity in C.
- Zambyte 2y agoEither -> is a binary operator or is is not an operator in the expression at all, and the unary postfix operators are ->b, ->c, and ->d. In the latter case, that gives us (->d (->c (->b a))) in Lisp, which I can see how ypu could get, but I don't agree. #include <stdio.h> #include <stdlib.h> struct foo { int a; }; int main() { struct foo *f = malloc(sizeof(struct foo)); f->a = 5; printf("%d\n", f->b); free(f); return 0; } test.c:11:21: error: no member named 'b' in 'struct foo' 11 | printf("%d\n", f->b); | ~ ^ 1 error generated. What do you mean when you say fields are not first class in C?