4 ms·
> I can look at blurry C code [...] I'm kind of curious how much of this is the problem with parens, and how much of this is the conventions that Lisp programm
by xscott 4y ago
> I can look at blurry C code [...]
I'm kind of curious how much of this is the problem with parens, and how much of this is the conventions that Lisp programmers seem to mostly adopt. For instance, here's the concise 99-bottles from Rosetta Code for Racket:
(define (sing bottles)
(define (plural n) (~a n " bottle" (if (= n 1) "" "s")))
(printf "~a of beer on the wall\n~a of beer\n~
Take one down, pass it around\n~a of beer on the wall\n\n"
(plural bottles) (plural bottles) (plural (sub1 bottles)))
(unless (= 1 bottles) (sing (sub1 bottles))))
(sing 99)
And even with a snippet this short, it's not quick for me to scan. Lispers generally like hanging parens, the indentation varies depending on what construct you're in, and that example uses recursion instead of a for-loop [0].
Ok, so let's turn the concise Python one into a hypothetical Lisp:
(for i in (range 99 0 -1)
(let b = "bottles of beer")
(let w = "{b} on the wall")
(print "{i} {w}, {i} {b}\n")
(print "Take one down and pass it around, {(- i 1)} {w}.\n")
)
I think you could "know generally what things are" with that version even if it was blurry. And of course, this "hypothetical lisp" is legal Racket/Scheme after implementing a few macros.
[0] I know Racket has for loops, but they didn't choose to use them in that example. Which is my point.
- deleted 4y ago[deleted]
- kazinator 4y agoMore or less idiomatic TXR Lisp: 1> (defun bottles-verse (n) (flet ((bottles (:match) ((0) "No bottles") ((1) "1 bottle") ((@x) `@x bottles`))) (put-line `@(bottles n) of beer on the wall\n\ @(bottles n) of beer\n\ Take one down; pass it around\n\ @(bottles (pred n)) of beer on the wall\n`))) bottles-verse 2> [mapdo bottles-verse 5..1] 4 bottles of beer on the wall 4 bottles of beer Take one down; pass it around 3 bottles of beer on the wall 3 bottles of beer on the wall 3 bottles of beer Take one down; pass it around 2 bottles of beer on the wall 2 bottles of beer on the wall 2 bottles of beer Take one down; pass it around 1 bottle of beer on the wall 1 bottle of beer on the wall 1 bottle of beer Take one down; pass it around No bottles of beer on the wall nil 3> Why does 5..1 go from 4 to 1? Because 1..5 goes from 1 to 4; it's a half-open interval. 5..1 goes over the same values in reverse. :match is a "parameter list macro", which is something I invented. flet knows nothing about pattern matching; it transforms to a lambda, which also knows nothing about pattern matching, but :match takes the parameter list and body, transforming the code using the pattern expander logic. String literals and quasiliterals can go across multiple lines, but with the permission granted by a continuing backslash. Being explicit about this lets us catch errors when a literal is left open accidentally. All unescaped whitespace before a continuing backslash, and leading whitespace at the start of the next line is deleted: "abcd \ efgh" -> "abcdefgh" "abcd \ \ efgh" -> "abcd efgh" ;; escaped space counts "abcd\ \ efgh" -> "abcd efgh" ;; on either side of \
- xscott 4y agoI think I must be really bad at communicating, because you aren't responding to any point I was trying to make. Let me try again :-) I think Racket, Scheme, and some Lisps are conceptually elegant, but they don't read well to people like me (or apparently the poster above). In particular, your snippet of code has 3 different indentation strategies, and you hang your closing parens the way almost all Lisp programmers do. To me, it all looks so organic and arbitrary. So, for those of us who don't like to read other people's Lisp - is it because of the parens? Or is it because the conventional Lisp style is alienating to us? I really think it's the style. I'd rather read your example like: (defun bottles-verse (n) (flet ((bottles (:match) ((0) "No bottles") ((1) "1 bottle") ((@x) `@x bottles`) )) (put-line `@(bottles n) of beer on the wall\n\ @(bottles n) of beer\n\ Take one down; pass it around\n\ @(bottles (pred n)) of beer on the wall\n` ) ) ) That's still not quite as nice as I'd like, but maybe you can see where I'm coming from. I think most Lispers would say, "You get used to it." But, well we don't get used to it because we just stay with whatever our curly braced or indentation language is instead.
- kazinator 4y agoI write Lisp mostly in the same I write C, and the way I wrote C before learning Lisp. (Except for not closing braces on the same line; though I experimented with that style and it was fine.) printf("Hello, %s\n", get_user_name()); In C, it is common to close parentheses together though. Look at the alignment in the tm_diff function in glibc: https://sourceware.org/git/?p=glibc.git;a=blob;f=time/strftime_l.c;h=75554fee7ceff4606c81a724216e054a2cb47d44;hb=HEAD#l352 https://sourceware.org/git/?p=glibc.git;a=blob;f=time/strfti... 371 int days = (365 * years + intervening_leap_days 372 + (a->tm_yday - b->tm_yday)); 373 return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour)) 374 + (a->tm_min - b->tm_min)) 375 + (a->tm_sec - b->tm_sec)); Vim does this alignment; I'm confident I could visually select these lines, hit = and they would stay the same. The author put extra parentheses around the initializer for days, and around the return expression. I almost certainly know the reason; doing so encourages the editor to do the alignment of the + operators with the interior of the parenthesis. Vertical alignment is important because if you violate it, it leads the eyes astray: `@(bottles) @(bottles)` Both expressions are inside the quasiliteral: they are parallel elements. Why has one de-indented, as if the literal had already closed in the previous line? There could be another argument after the quasiliteral: (put-line `@(bottles n) of beer on the wall\n\ @(bottles n) of beer\n\ Take one down; pass it around\n\ @(bottles (pred n)) of beer on the wall\n` output-stream) If you have it like this: (put-line `@(bottles n) of beer on the wall\n\ @(bottles n) of beer\n\ Take one down; pass it around\n\ @(bottles (pred n)) of beer on the wall\n` output-stream))) you're pepetrating a trompe l'oeil. The transition out of the literal is masked, making it look like output-stream is still part of the literal. I see you like four space indents, and I've used them in C in years past. Even if I wanted to experiment with four space indents in Lisp, the editor I use wouldn't support it. Believe it or not, the two space indentation is a hard-coded behavior in Vim's Lisp indent mode. Vim doesn't support the quasiliteral formatting I'm doing; I had to make the one space adjustment by hand. In short, there is almost nothing arbitrary about the way I'm formatting anything.