puts("Hello")
to (puts "Hello")
Or for example if (i < x) {
puts ("low");
} else {
puts ("high");
}
to (if (< i x)
(puts "low")
(puts "high")
)
Is very complex. puts("Hello")
to (puts "Hello")
Or for example if (i < x) {
puts ("low");
} else {
puts ("high");
}
to (if (< i x)
(puts "low")
(puts "high")
)
Is very complex.Prefix notation is hard, at least for people whose native language is S-V-O, and not V-S-O.
So
i < x
reads naturally: "i is smaller than x". On the other hand: < i x
reads unnaturally: "is smaller than i x". It's not a huge deal individually, but it does add up. And of course the extra level of parens is necessary(?) in LISP."if" is interesting, because it seems to do fine in prefix notation, and puts() is also a bit weird because its subject is missing/implied:
stdout puts:"hello".
But in general I find it more surprising that prefix-y notation is as acceptable as it is for most function calls, despite the fact that, when offered, infix notation is considered more "natural" or even more "built-in", than that applying it to even more situations is considered difficult. (< 1 (+ 2 3))
collapses to (< 1 5)
collapses to true
Mentally when scanning Lisp code, I only hold the data in my headed needed for the current function, then I 'collapse' into the return value and swap out the old data in my working memory for the new data. It's worth noting that this collapsible style is why Lisp code can be so consistantly concise: (let [a 1
b 2
c 3])
Or updating multiple items in a dictionary in one go: (assoc dict :key1 val1
:k2 5
:k3 "value"))
So while the prefix notation seems odd at first, if you stick with it you can see how it really makes your life easier in the long run.In
(let [a 1
b 2
c 3])
vs let a = 1,
b = 2,
c = 3
or let a = 1, b = 2, c = 3
or even let a = 1
let b = 2
let c = 3
there's a saving in the lisp version of a token or two, at the expense of having additional meaningful structure / nesting. This isn't inherent to lisp, of course; there could well be a lisp that treats commas or some other visual separator as whitespace and thereby allows (let [a 1, b 2, c 3])
or (let a 1, b 2, c 3)
...but in general the benefits of lisp are about power, not formatting, in my opinion.Really? Lisp is the most concise and clear language I've ever used. There is almost zero boilerplate when writing it.
The example was a simple one to get the idea across, you can create your own functions and do the same too:
(my-let [a 1
b 2
c 3])
my-let could be a macro that compiles to a normal 'let', while also recording all variable names for some other use, or adding logging, or whatever you want. Another concise example is the -> macro to pass the result of a function in as the first arg of the next function: (-> a b c d e f)
instead of (f(e(d(c(b(a))))))
These can all be argued against I'm sure as they are still simple. Check out some example code written in Clojure for a better view of conciseness [1][1] https://github.com/oakes/play-clj-examples/blob/master/break...
You mean at the expense of losing the meaningful structure?
I.e. the token count is one token smaller between these examples, in favor of the Lisp:
(let [a 1
b 2
c 3]) ;; 11 tokens
let a = 1,
b = 2,
c = 3 // 12 tokens
but this comes from flattening the expression for the variable bindings.We have no idea if/when the variables in the second version go out of scope.
This is a huge problem in languages that aren't lisp.
If it weren't it would just be a def/setf/defvar/defparameter.
In the example given, assuming let has the same properties, we have no idea when the second 'let' statement ends, you would need something to signify the end of the block... there isn't anything in the given example. It could be implicit whitespace or something, but that isn't clear.
The crystal clear point of ohyes's observation is that the token count is unequal, in a sense: the Lisp expression is defining variables and delimiting the scope, and includes tokens which establish scoping, whereas the Algol-like snippet is only defining variables, letting an unspecified surrounding program delimit their scope. That program is going to require some syntax in order to delimit that scope; that syntax is not shown and so its tokens are not counted.
I get that you think having to wrap your entire program in an extra set of delimiters is super special, but it's really fucking not.
If it were truly algol it would probably have an 'endlet' symbol at the end of the let, for example. If it were C it would require curly brackets, python out-dentation, ocaml something else(!?).
Anyway, I suspect you're a troll,
good day.
That is, the complaints you are naming go all the way to how "non-programmers" can't read most programs. They are much more rigid in structure than normal sentences, and to pretend that C or other syntax is somehow more natural misses that they all take training.
This is also why some languages had a specific "then" in their syntax. Most have dropped that, since it could often be completely inferred. (And many languages have an "unless" or similar construct.)
All of that is to say my point is that you are only used to the other way. It is not more natural in most any sense of the word.
Note that in the imperative mood, English is normally VO, with an implied second person subject.
That's pretty much exactly what prefix notation is.
In “add six and two”, “add” is the verb, “six and two” is a composite direct object, and the subject (“you, the reader/listener” is implied.) This maps quite nicely to (add 6 2).
Parentheses are also visually designed. We could make a languagein which ) opens a list, and ( closes it. The machiine wouldn't care, but the code would look like this:
)defun add )a b(
)+ a b((
Visual matters!Here is a mental trick that might help with < and > in Lisp reading. Forget the relationship to the math symbols, and think of it as music symbols:
< is crescendo (play gradually louder):
> is decrescendo.
(People do complain about prefix vs infix, but its very much drowned out by the people who complain about parentheses)
Infix notation only works "naturally" for two operands.
Or n operands where the operator is the same (think a + b + c + ... ).
The Readable Lisp project for Lisp without parentheses allows using infix notation in Lisp for these two use cases.
a raisedTo: 2
vs. a ** 2Infix mathematical and logical notation is a tiny part of the code that I write. More complicated mathematical productions lead me to fully parenthesize the statement anyway.
Further, to my knowledge there isn't a high level 'fully infix' language. I suspect it would be pretty difficult to use.
the parenthesis argument is literally:
verb(nouns) + some syntactic sugar
vs.
(verb nouns) + some different syntactic sugar that's often user defined.
This is an excellent argument for buying my children HP reverse polish scientific graphing calculators. I think I had a 48 series. Get your kids into the church early.
I also think that comparing programming language grammar to natural language grammar isn't helpful at all. Fluency is moving beyond the point of having to translate everything to your native language in your head on the fly.
IIRC racket allows you to write (x . < . 5), and some schemes have curly infix (a srfi) where {x < 5} gets translated to (< x 5).
print(sin(pow(2, x)))pow() is a good example, as it was used a lot as an example for operator overloading in, for example, Swift. So instead of
pow(2,x)
you can write 2 ** x
For many people, the latter seems to be more natural, and feels more "built in" on the one hand, but on the other hand having lots of weird combinations of special characters is somewhat unreadable.If you extend infix syntax to named methods, the problem goes away:
2 raisedTo:x
So why is prefix function notation so popular? The only reason I can think of is that it mimics the function notation we learned in school: f(x). And I guess something can be said for separating the function from its arguments.So in a sense I agree with you that the difference is not huge for some expressions, but for me that doesn't solve the mystery, but only shifts it.
Also, infix has the unfortunate problem of requiring operator priorities to parse.
f(2, g(3, 4))
is impossible to misunderstand. 2 f 3 g 4
isn't.(2 f (3 g 4))
which is arguably how most sane engineers would write it.
> which is arguably how most sane engineers would write it
I'm always wary of this sort of statement.
(< a b c) := a,b,c are ordered from smaller to larger
And this allows for multiple comparisons, which is useful.
Edit: < could be called increasingp
Example:
(remove-if (op < 3) list)
that removes items from list if they are greater than 3 because they are inserted as a second operand (< 3 item).But the syntax invites a misreading as "less than three".
> Edit: < could be called increasingp
How about: crescendo!
I don't like using it in this case because that amounts to admitting there is some kind of problem. I'd rather fix the cognitive root cause in the brain.
It's very useful, for two arguments it works just like x < y, and it makes you think about (< x y) as "in rising order: x and y"
I have never found or read about any lisper (any programmer who wrote useful, complete code in Lisp) complaining about parenthesis or syntax. They all either love it or at least don't complain about it.
(if (< i x)
(progn
(puts "low")
(puts "high")))