D-Edit was already around when I got to PARC in late 1983. The first thing I worked on was an Emacs in Interlisp-D.
I think ITS Emacs (written in TECO) had paren matching before the CADR Lisp machines (what you call Zmacs, but it was EINE originally).
The first Emacs written in Lisp was Multics Emacs (written by Bernie Greenberg) and it had paren matching when I used it but that was 81/82 timeframe.
What do you call paren matching by the way? Emacs always had structure navigation (c-m-F/B and c-m-U etc) in which case that support went back into the 1970s.
(BTW the first Emacs-like editor for the lispms was EINE (Eine Is Not Emacs); it was replaced by ZWEI (Zwei Was Eine Initially). Look at a German dictionary if that is not funny. IIRC Zmacs was a renaming by Symbolics as part of their efforts to move beyond the AI lab implementation (While LMI/TI continued to sync back to the AI lab sources).
EINE was the first Emacs written in Lisp. Actually Lisp Machine Lisp by the then young Dan Weinreb at the age of 17. Dan passed away way too early a few years ago.
Hi Gumby! You're missing a close paren at the end there :-)
ETA: are you sure about the timeline? I thought Multics Emacs was something of a reaction to EINE/ZWEI -- I seem to recall that Bernie didn't like the fact that all the editing primitives required an explicit point argument; he made point implicit, a design decision that was copied in Gosling Emacs. Well, that's my recollection anyway.
Teletype Lisp editor was really amazing experience. I got the idea I could do something similar to natural languages. Especially Finnish Language, which contains very long words, and the end of those words do not necessarily need to be printed out to aid comprehension. So chapters and paragraphs and sentences would be like S-expressions in Interlisp Editor, only first few words showing and first few letters printed out in worlds. Unfortunately this grand idea never came real, because they did not give me extra teletype time in 1974.
I found this the most interesting. For one thing reminding me of Erik Naggum's existence, insight, and causticity[1], and for another exploring what seems to be a promising but dead branch of programming.
As we continue wishing we had "better ways to write programs", constantly re-exploring visual programming (Scratch and its spinoffs), it's interesting to me that the ALGOL-children (C and the like) have completely lost this way of developing programs. What is there to learn, both from the technology itself and from how we forget?
I started to use paredit early in my Lisp career and never looked back. I tried smartparens but found it annoying (I find it much harder to change my tools these days).
Parinfer looks very clever, but I'm not afraid of "hotkeys" (I use emacs) and I don't want to do manual indentation. Is there any benefit over paredit for me?
I'm a paredit user too and I tried but couldn't make the switch to parinfer. I think there's just too much muscle memory I'm fighting against. But I've heard that parinfer was much easier to pick up from two separate people that were just learning Clojure, for whatever that's worth.
I also use paredit. I haven't learned many of the advanced operations or keyboard shortcuts, it does the right thing intuitively just by using open/close paren and forward/backward/kill line/kill word etc.
EDIT: I don't mean that to sound critical of parinfer development. Developing the software on top of a formal relationship between indentation and parens sounds like a very interesting and laudable approach.
What I'd love to see is work on producing relaxed modes inheriting from paredit for use in non-lisps such as python.I use a very slightly modified paredit for python and have done every day for years. It gets it 95% right.
I too stucked with paredit, it's a very beneficial sweet spot. Parinfer adds lots of sugar, sometimes very helpful (switching layout, automatic ones). I'll give it a spin but I'm not sure I'll drop paredit (maybe because of loyalty)
I don't use paredit or similar extensions to code Lisp. Just bare Emacs (with SLIME, of course) is enough. The closing parens that I didn't type are distracting. If there's so much parentheses in your code that you're tired of closing them, maybe it needs a refactoring?
This comment misses the point entirely. The point isn't to mitigate having to type them, it's to help keep structural preservation. All of these structural editors also do so much more than just close the parens. They allow you to edit the code as the tree it is as well as navigate it as such. Without the parens actually being there, you couldn't do that in a determininistic way, thus the auto-closing of parens is more of a side effect than anything else.
You actually don't need parentheses--you can use whitespace. You give up the ability to add N arbitrary trees/nodes to one line, but in return you get beautiful, geometric code that's easier to reason about at scale.
Indeed. Or to simplify:
- At no point during the editing process do you have an invalid s-expression.
- You manipulate the code datastructure, not the code text.
I'd like to find out how people tolerate ()[]{};,. in their programming, in far greater quantities and in more complex interspersal than Lisp's parens. )]];));}}};
You know, I think it's a fine argument to say, "Yes, Lisp has a lot of parentheses but we like how it looks." I don't think it makes sense to say, "No, other languages have more punctuation characters than Lisp." The latter is an objective statement that can be actually tested.
I went to Rosetta Code and grabbed the implementations of Conway's Game of Life in Racket, Ruby, Python, and Java. For each one, I manually removed comments and string literals. Then I wrote a little script. It looks at each character in the file and groups them into categories:
The Racket implementation has roughly twice as many bracketing characters as the other languages, and more of all kinds of punctuation than any of them. It may be that the languages and example programs I chose aren't representative, but I'd be surprised if another corpus was significantly different.
Most languages have multiple levels of precedence. The entire point of that is to allow eliding explicit bracketing characters for commonly nested subexpressions. You may dislike that feature, which is reasonable. But I don't think you can argue that it doesn't exist and doesn't accomplish what it intends to do. Consider:
I think most of the non-bracket "punctuation" in Racket is caused by the kebab-case identifiers and to a lesser degree by ? and ! in function names.
So in fact "Other languages have more punctuation characters than Lisp." is not an objective statement at all, since it relies on the definition of punctuation, which is subjective or at least language-specific.
Good point. I updated my little script to (roughly) take each language's identifier rules into account. Now "?" is considered an identifier character in Ruby and "-", "!", and "?" are punctuation in Racket. It gets a little fuzzy, of course because ">" works like punctuation in "(> 1 2)" but less so in "(number->string 123)".
This gets Racket down to having similar levels of punctuation but note that, of course, brackets are still significantly more common than other languages.
Personally, I would remove =+-*/ from punctuation. They're generally meaningful common & fundamental operators, not just groupers/separators/terminators. And as mentioned, in Lisp, symbols commonly contain dashes (and less commonly other punctuation), including standard-function-names, which would skew the results. I'm not super familiar with Racket, but presumably it retains this syntax style.
But generally, Lisps aren't intended to be compact in the small, but semantically regular, which can be a definition of "simpler", and give smaller code in larger systems. Source code compression for syntactically-atomic operations is where all the other varied punctuation comes from, which addresses snippets of code, not large abstraction gains. So yes, there are cases where Lisps will have more parens than other languages have total punctuation. Even in such cases, the other languages are more complex to read, both for humans and for machines.
Note that I didn't only mention quantity, but also more complex interspersal. Most other languages have commas for field separators, while Lisps tend only to use naturally-occurring whitespace. For instance, f(a,b,); is commonly an error, while (f a b ) is valid Lisp syntax, as a low baseline example. Prolog-derived languages go even more nuts with ; and . than C-style terminators. And the brackets vs braces vs parens all nest together. The }]);}]); style closing mess is very common in JavaScript, where objects and function objects nest heavily due to its callback-centric style.
Other languages also have a mix of infix, postfix, and prefix ordering:
fun5((fun1() op3 fun2()).fun4()).fun6();
The function calls & operators are numbered by their final execution ordering (assuming op3 isn't short-circuiting). This is just messy to read and edit, again both for humans and machines.
And the biggest reason why things are so much more regular in Lisp is metaprogramming, code generation, and code transformation. This is such a pain in the butt in other language's direct source code representations, unless separate ASTs are involved, and ASTs aren't editable as plain source code for template-style use. Separate libraries could tackle this, but they would have to be maintained in lockstep with the language itself, and manually replicate every subtle nuance of the language to avoid bugs (including replicating reference implementation bugs).
So this whole thing is about technical complexity of representation, not just personal preference.
> Prolog-derived languages go even more nuts with ; and . than C-style terminators.
What do you mean by this? In Prolog, (;)/2 is simply a binary infix operator, so that you can write a term of the form:
;(A, B)
i.e, a binary term whose primary functor is a semicolon, equivalently as:
( A ; B )
where the semicolon now occurs as infix operator instead of at its canonical position at the beginning of the term.
In Prolog, you can always use the predicate write_canonical/1 to obtain the canonical representation of any term, which is completely regular. In the case above, we get:
?- write_canonical( (a ; b) ).
;(a,b)
This confirms that (a;b) and ;(a,b) denote the exact same Prolog term which cannot be distinguished at the AST level.
We have also several other ways to inspect the term, such as functor/3:
Thus, '.' is used to mark the end of clauses, since each clause is a valid Prolog term.
If you want, you can always use the canonical representation of Prolog terms in your code. However, this would make writing and reading Prolog at least as inconvenient as Lisp, because you then have to write everything in prefix notation instead of being able to benefit from infix operators too.
For example, using prefix syntax, you would define a Prolog rule as:
:-(Head, Body).
whereas with infix syntax, you can write the exact same term equivalently as:
Head :- Body.
Similarly, you would have to write:
#=(X,+(5,3))
instead of using the typical infix notation for arithmetic predicates and expressions:
X #= 5+3
Note that since the respective abstract syntax trees are completely the same, Prolog remains at least as amenable to metaprogramming as Lisp even though Prolog supports more flexible syntax!
Yes, I do like the fact that Prolog infix operators are just optional sugar for standard prefix definitions.
In talking against separators, in "Prolog-derived" languages like Erlang (and other custom derivations I've dealt with), separators are used even between clauses. Most other languages use separators just for fields, and use straight terminators for statements. Prolog itself gets a pass because these separators are literally ANDs and ORs which flow as logical expressions through the backtracking search.
Even toplevel expressions defining different match parameters for the same head require differentiating separators from a final terminator for that head in Erlang, whereas Prolog just deals with independently terminated head matches just fine. The Erlang code is much more removed from a logical AND/OR flow into more standard programming models, yet is still separator-based. Instead of thinking of the logical, partial evaluation possibilities of
x AND y AND z
it's a more traditional
statement 1,
statement 2,
statement 3.
with inconsistent characters at the end of each statement, when looking at it from a statement-centric point of view. Editing such code always requires a scan of the context of inter-statement relationship (especially when nesting) instead of just treating & terminating statements independently.
I think it's about visual parsing. They may be greater in number in C derived languages but it's not idiomatic or considered clean by anyone to write it as you did, where it's typical to see things like )))))) in lisp.
If there were multiple markers stacked up in a C based language they'd be visually distinguished from one another ];} and you'd at least have a chance.
Look, we can get used to anything so clearly the lisp way can work. But it seems pretty straightforward to me why people think it's visually confusing and why lisp gets made fun of. Simplicity in syntax is great but lisp really overdid it. If you invented a human language that could get by with only 12 letters, I wouldn't give you kudos for that because of the general principle that simplicity is good...
There have been at least three attempts to change the syntax of Lisp, so that it looks more like Algol: Lisp 2, CGOL, and Dylan. None of these gained much traction. Lisp programmers prefer the parentheses.
When you autoindent, the indentation of the code makes its underlying structure (AST) clear without having to count either parentheses in Lisp or braces in C. By the 1970s, autoindenting editors (EMACS) and structure editors had been developed for Lisp. In the C/Unix world, until recently, indentation and parenthesis/brace counting had to be done by hand. Among C programmers (but not, to their credit, Java programmers) there are still arguments about code layout, a problem solved decades ago for Lisp.
Ta. I just tried it. If it's all the same with you I think I'll stick with Emacs. :-)
What I was trying to say is that these things are easier to do by hand in C than in Lisp, but more difficult to do algorithmically, and this discouraged people developing syntax-aware editors, and encouraged the plethora of competing C styles.
In the Lisp world, the difficulty of doing these by hand resulted in earlier syntax aware editors and the consensus on style.
The thing is, if you actually set yourself out to create an application in Lisp, and you do learn the language and the tools made for programming in that language, not only you will have no problem with parentheses...
... but you also will find that most of your code is highly readable!
Lisp code (or at least Common Lisp code) can be highly readable. I never need to count parenthesis to understand code. The indentation itself makes the structure obvious. And if there is any doubt, the editor (say, Emacs) will tell me where each expression ends.
> "But it seems pretty straightforward to me why people think it's visually confusing and why lisp gets made fun of."
Yeah, actually it is. That despite the examples being seemingly somewhat contrived and the comparison also not so good.
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.
This is a common misconception non-lispers believes exist. Once you've written Lisp for a couple of months these problems disappear. The trick is not to try reading it like an English sentence, but see it as a collapsing tree of functions:
(< 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.
Lisp has a lot of things going for it. Being "concise" is not one I've seen argued, much.
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]
in any reasonable implementation of lisp the let is implemented with lexical scoping. This is because it is the transformation of a closed over lambda, and lambda arguments have lexical scoping akin to a stack push/pop.
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.
This is something other languages do as well. Lisp zealots seem to think every other language is banging rocks together. Lexical scoping/closures/whatever are traditions independent of language or implementation. Nobody but you is having trouble understanding the scope of the algol example.
Nobody but you is having trouble understanding the observation made by ohyes that the Algol syntax is a fragment which can be embedded into a larger scope which could declares more variables. Whereas the parenthesized let is a complete construct which opens and closes its scope. We know where the scope begins and ends, and its full content.
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.
Um, so fucking what? I could put a pair of braces or parens around the algol program, too, but they are implicit. Or is that too exotic a concept for your lisp-addled mind to accept?
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.
The 'algol' example doesn't have enough context to demonstrate that there is a lexically scoped block. The lisp example contains an empty block.
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(!?).
This is something that only exists because of practice.
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.
> Prefix notation is hard, at least for people whose native language is S-V-O, and not V-S-O.
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).
Well, < and > were designed visually to work in infix situations. The pointy end goes to the small thing; the wide end goes to the big thing.
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:
I rarely hear people complain about prefix notation though - people typically complain about PARENTHESES, and with parentheses, there's really not much difference.
(People do complain about prefix vs infix, but its very much drowned out by the people who complain about parentheses)
Indeed, I often catch myself swearing at the visual mess created by the use (and abuse) of parentheses (and all kinds of other brackets), the mess you have to deal with when coding in C++ - especially with lambdas, but also with any sort of nested code - classes, function calls, etc. At these moments, I can't help thinking that Lisp code would look cleaner and more readable, while the C++ syntax, by contrast, looks ad-hoc and inconsistent - especially with the growing context-dependency of the meaning of symbols like the ampersand, the asterisk, the square brackets, etc.
Those would probably be considered "mixfix" more than "infix". I think most people who use "infix" use it to refer to binary expressions with two operands.
Calling keywords "infix operators" is a bit disingenuous, they are just labels used to mark off different arguments in an otherwise normally looking method call.
Infix 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.
You're misreading (< i x) as "is smaller than i x".
< means the "the list is in ascending order", and > means "the list is in descending order". i < x is using a binary infix operator but (< a b c) is calling a variadic function. I think trying to mangle the natural language description of the former to describe the latter is a mistake.
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.
I like to read my code in plain english too but I tend to change how I make a sentence from code when dealing with prefix notation. Like instead of "is smaller than i x", I read that as "compare i and x". Of course that doesn't hold for all cases. I just find I have to change my thinking a bit when dealing with Lisp and OCaml and C, making the way I read my code change too.
For expressions I can understand it's harder. But for function calls we already use prefix notation in almost all languages. We just place the parentheneses differently.
Yes, and that's the odd part, right? That this prefix notation is considered "natural" when there is lots of evidence that it isn't.
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.
For me it's just a matter of what you're used to. We're used to 2+2, and sin(x) so that's what we do. If you learned +(2,2) then that's what's more intuitive for you. And with time you get used to new approaches anyway.
Also, infix has the unfortunate problem of requiring operator priorities to parse.
Infix doesn't require operator precedence, it's just something we've adopted from math. Notice that you've flattened the parse tree in the second example. A fair comparison would be:
(2 f (3 g 4))
which is arguably how most sane engineers would write it.
I agree - I write lisp for probably 50% of my time, and I have to juggle comparisons in my head every single time. For some reason it's much harder than arithmetic functions.
I don't know of any op macro that doesn't have a way to insert the free argument(s) explicitly.
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.
Regarding "<" i love the way clojure solved this. It made (< ....) a vararg function that returns true if arguments are in strictly ascending order. And analogically other binary comparison operators.
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"
Apparently it is difficult, but in reality, as you progress in learning Lisp, it becomes second-nature and really easy.
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.
Tangentially: I use a variation on this, but I do it as a custom keyboard layout system-wide. From a US keyboard, I rotate () → [] → {} in that order (associated character → new position), based originally on frequency analysis of X captures of my keystrokes from a decade or two ago. Nowadays I also swap ` over to F9 and rebind the ` key to Compose (leaving ~ alone), since I use it a great deal for extended punctuation.
I incorporated parinfer into my reagent live coding environment here[1], and it's been working pretty well.
I have also used Atom with Parinfer to teach a nontechnical designer the basics of clojure. Enough to build templates using hiccup for her resume, and personal website. I don't think smartparens or paredit would be anywhere near as easy for her to pick up.
If you're used to indentation being meaningful as some langs do, you should probably try out clojure/script with parinfer.
Would be nice to see some examples on that page about how to actually use parinfer commands. Or maybe you have that explained on a different page?
edit: I see, the point of parinfer is to not use commands or hot keys. I'm going to play with your site and if the slurp/barf behavior is any better than paredit.
But probably worth spending some time with ITS, TOPS-10, and TOPS-20 & digging around with TECO and its descendants which came to be emacs, and probably checking into 4.2/4.3BSD as well..
The former group is pretty much the platform for early LISP (when lisp was still in caps :b ), and the latter, well, alot of good stuff happened here.. plus built in franz lisp and the lisp mode for (real) vi ..
Very interesting! paredit + agressive-indent made my life so much easier. Now I don't have to worry about parens OR indentation. Unless you work with people who have different preferences of course, but for my own stuff I generally stopped trying to fight agressive-indent (at least it's consistent).
I think ITS Emacs (written in TECO) had paren matching before the CADR Lisp machines (what you call Zmacs, but it was EINE originally).
The first Emacs written in Lisp was Multics Emacs (written by Bernie Greenberg) and it had paren matching when I used it but that was 81/82 timeframe.
What do you call paren matching by the way? Emacs always had structure navigation (c-m-F/B and c-m-U etc) in which case that support went back into the 1970s.
(BTW the first Emacs-like editor for the lispms was EINE (Eine Is Not Emacs); it was replaced by ZWEI (Zwei Was Eine Initially). Look at a German dictionary if that is not funny. IIRC Zmacs was a renaming by Symbolics as part of their efforts to move beyond the AI lab implementation (While LMI/TI continued to sync back to the AI lab sources).