All Lisp indentation schemes are ugly
https://aartaka.me/lisp-indent.html (define (match:element variable restrictions)
(define (ok? datum)
(every
(lambda (restriction)
(restriction datum)
)
restrictions
)
)
(define (element-match data dictionary succeed)
(and
(ok? data)
(let ((vcell (match:lookup variable dictionary)))
(if vcell
(and
(equal? (match:value vcell) data)
(succeed dictionary)
)
(succeed (match:bind variable data dictionary))
)
)
)
)
element-match
)
It may not be densest or most compact indentation scheme, but damn is it readable for someone without a lisp/scheme background!Likewise, a lispier(?) and more compact compromise?:
(define (match:element variable restrictions)
(define (ok? datum)
(every
(lambda (restriction)
(restriction datum))
restrictions))
(define (element-match data dictionary succeed)
(and
(ok? data)
(let ((vcell (match:lookup variable dictionary)))
(if vcell
(and
(equal? (match:value vcell) data)
(succeed dictionary))
(succeed (match:bind variable data dictionary))))))
element-match)
This looks like how I've naively tried to format things, and having started with python kind of makes sense.
But trying to read / edit code with 5 parentheses bunched together, I have the hardest time telling if they are matched or where. Thank goodness for vim / helix auto-matching.
Honestly though, this style for ending parenthesis of block-type nodes makes a lot of sense to me. And you still retain the benefit of macro-ing your own syntax when you need it.
(
define (match:element variable restrictions)
(
define (ok? datum)
(
every
(
lambda (restriction)
(restriction datum)
)
restrictions
)
)
(
define (element-match data dictionary succeed)
(
and
(ok? data)
(
let ((vcell (match:lookup variable dictionary)))
(
if vcell
(
and
(equal? (match:value vcell) data)
(succeed dictionary)
)
(succeed (match:bind variable data dictionary))
)
)
)
)
element-match
)
Granted I'm not the one to optimize for since I don't use Lisp, but this just seems sane and easy to automate, and it looks like other non-Lisp languages.
I'll happily tolerate some wasted space in that trade-off.
(I hope?!)
My "OTBS all the things" rules:
- Never horizontal align. Indentation is a simple tree, with each set of children indented 1 layer deeper, which can be 4 spaces or 1 tab.
ThisKindOfThing(
foo,
bar,
baz);
is generally dumb.- If a block is worth indenting for, the end of the block deserves its own line. Just do OTBS but also for parentheses if you've a long function call.
ThisKindOfThing (
foo,
bar,
baz
);
Is just more consistent and easier to manage while still being legible.- if you're ever splitting something across multiple lines there must be an indent.
myObj
.Foo()
.Bar()
.Baz();
- you're allowed to double-dip on tree depth - that is indent only one level for like 3 levels of tree, as long as the opening of that 3-deep jump is a clear one-liner and the end of it is a line that says ")))". Foo(Bar(Baz(
someVal
)));
- when splitting lines on infix operators, anything but commas go at the start of the line (also, bedmas counts as tree depth) foo
+ bar
+ baz
+ (
var1
* var2
* var3
)
+ quux;
(tree-transform-if predicate
transformer
(second tree)
depth)
> A problematic function indentation> Nineteen! Nineteen spaces of indentation! It's getting unruly.
Why, is there a shortage of whitespace these days??
> Such an indent, when used in deeply nested code, makes it too wide and unreadable.
Ah, I see! Well, I would argue the problem is not the indentation style. What makes your code unreadable is that it's too deply nested, and I would bet that no indentation style can help with that.
> (tree-transform-if predicate
> transformer
> (second tree)
> depth)
> A problematic function indentation
> Nineteen! Nineteen spaces of indentation! It's getting unruly. Such an indent, when used in deeply nested code, makes it too wide and unreadable. If you add the strict one-per-line alignment of arguments, it's also painfully long line-wise.I think this is more a problem with languages that encourage (or don't discourage) deeply nested function calls. I face the same issue in Python, although Python kinda discourages me to do:
foo(
bar(
baz(
far(
faz(...)))))
Instead, Python and Pythonic idioms encourage a "step by step" approach: a = faz(...)
b = far(a)
...
I don't know which is "better"—the first approach requires thinking ahead about the chain of functions we're about to call while the second one is more like a gradual development of the solution, albeit being more verbose than the former.