But compare to, say, C where no one complains about the parens:
int fact(int n) {
if (n==0) {
return 1
} else {
return n*fact(n-1);
}
}
That's six pairs of parens. The only reason it's not more is because some function calls in C are disguised as operators, and those just happen to be the functions that factorial uses. If we wanted a semantically equivalent function that handled bignums you'd have to write: bignum fact(bignum n) {
if (bignum_is_zero(n)) {
return n;
else {
return bignum_multiply(n, fact(bignum_subtract_1(n)));
}
}
Now you've got nine parens. But still no one complains about C because of that.It makes a lot of sense when all functions are curried. Parenthesised function calls treat all of these as different things:
f(a, b, c, d)
f(a, b, c)(d)
f(a, b)(c, d)
f(a, b)(c)(d)
f(a)(b, c, d)
f(a)(b, c)(d)
f(a)(b)(c, d)
f(a)(b)(c)(d)
If functions aren't curried, then these all mean different things. When functions are curried, all of these have the same semantics (call `f` with `a`, call the result with `b`, call that result with `c` and call that result with `d`); ML's juxtaposition syntax makes these semantically-identical expressions syntactically-identical too: f a b c d (curry f a b c d)You know how, when you're reading natural language, you don't actually read individual letters, not once you have any reading fluency? That's how I read code, too, I'm realizing. When I see the parentheses in C#, my brain just goes "oh that's a method/function call" and I can either skip over the contents of the parens or read them if I need to. Reading comprehension is pretty quick.
Looking at all the parens of even that simple lisp factorial, and I realize that I'm feeling a lot of cognitive load that would simply never go away, because the punctuation doesn't let me filter any of it out. I'd have to read everything more carefully.
Completely the opposite here.
Parens means function call or list. Lists usually denoted by a quote first.
I don't read the parens, I skim everything, reading words. And the punctuation is really simple.
For example:
if (number? a)
+ a b
error "Not A Number" a
I stripped most of the parens there, (like Wisp [0] does), because I don't really see them. I see function call and arguments, because there isn't anything else.Scheme has the very least syntax out of just about any programming language I've used. It has a tiny cognitive load compared to most.
As difficult as it is to imagine, you can indeed achieve fluency in reading Lisp code. Like with everything else, it takes time and practice.
Then you get the same amount of parens as fsharp. It is even standardised as an SRFI (although not widely.implemented)
(define (fact x)
(if (zero? x)
1
(* x (fact (- x 1)))
)
)
Square brackets work in some dialects, as well: [define (fact x)
(if (zero? x)
1
(* x (fact (- x 1 ]To spell it out: this means Emacs with Paredit, or an imitation thereof.
... until your coworker edits the file in the out of the box IDE and munges it.
I guess now-a-days, our languages and frameworks are so ugly they need a "bag" over their [inter]face while you are doing them :-)
I can read C++, Javascript and PHP just as well in plain text because the other non-paren elements provide necessary contextual cues, along with nesting. Syntax coloring is helpful, but it shouldn't be necessary.
I can at least understand nesting closing parens on their own lines, the way Roboprog did above, but throwing all of them on a single line just seems like needless noise.
And there's virtually nobody coding in a Lisp that doesn't use a structural editing mode (like Paredit or SmartParens). E.g., any time you type "(", you get "()", enforcing balance.
What we rely on is proper indentation to indicate the important groupings.
Wait, even Lisp does that for me, because it has a built-in source code pretty printer:
Call the pretty printer to format this mess:
CL-USER 41 > (let ((*print-case* :downcase))
(pprint '(defun add-text-padding
(str &key padding newline)
"Add padding to text STR. Every line except for the first one, will be
prefixed with PADDING spaces. If NEWLINE is non-NIL, newline character will
be prepended to the text making it start on the next line with padding
applied to every single line."
(let ((str (if newline (concatenate 'string (string #\Newline) str) str)))
(with-output-to-string (s) (map 'string (lambda (x) (princ x s) (when (char= x #\Newline)
(dotimes (i padding) (princ #\Space s)))) str))))
))
It prints wonderfully formatted and indented code: (defun add-text-padding (str &key padding newline)
"Add padding to text STR. Every line except for the first one, will be
prefixed with PADDING spaces. If NEWLINE is non-NIL, newline character will
be prepended to the text making it start on the next line with padding
applied to every single line."
(let ((str
(if newline
(concatenate 'string (string #\Newline) str)
str)))
(with-output-to-string (s)
(map 'string
(lambda (x)
(princ x s)
(when (char= x #\Newline)
(dotimes (i padding) (princ #\Space s))))
str))))
Now one would only need to fix this beginner level code.I am kind of developing a grudge against whitespace sensitive languages. Not because it forces the programmer to indent properly, but because it disables my editors abilities to do it for me.
I haven't done any actual Lisp since the 80s on a CDC Cyber mainframe and a dedicated expansion card on an Apple II, so I'm "a wee bit" out of date.
I'm very visual / "geometric", so I like to see the "shape" of things. I can't stand looking at stuff "formatted" (?) in the typical enterprise java (8) "staircase of doom" with minimal whitespace, incredibly long lines, with a few arbitrary line wraps that indent to random spots way off on the right.
I swear, many programmers have never considered how newspapers and textbooks are formatted. They don't sprawl text endlessly across even if the paper is wide, they keep it reasonably narrow so your eye can follow it.
It's such a shame because the development environment for Lisp could be so much better than other languages. It could be mindblowingly futuristic, instead of mindblowingly archaic.
It works like a charm. I can have a REPL and my code right in front of me, I can cmd-down to go to a function definition, and it's overall a very pleasant developer experience.
Now, I did experiment with Emacs, so I do understand what you're talking about -- kill-ring-save and the related key bindings made zero sense to me, and I was having trouble doing things I'd do easily in IntelliJ.
You are misinformed. I'm using a GNU Emacs (-> Aquamacs) on the Mac and cut/paste/copy are invoked with the usual command keys. But usually I use the LispWorks editor, which is also in the Emacs family, and even there cut/copy/paste is the usual command sequence... Both editors are built with excellent Lisp support. One is written in C + Emacs Lisp and the other one in Common Lisp.
> mindblowingly archaic
You haven't even understood the old part, how would you be able to deal with the futuristic part?
"You haven't used a PDP-10, how would you be able to deal with a smartphone?"
> If you actually count parens fairly you will find that Lisp has no more than any other language
Bull. For example, compare the Lisp factorial function from the article:
with the corresponding F# implementation: That's 7 vs. 1 pair of parens.Also, the problem isn't just that Lisp has so many parens, but that they nest so deeply and quickly. Five nested levels of parens for factorial alone.