# Lisp Indentation May 29, 2025 I've had the code snippets and a few paragraphs of this post in my drafts for over a year with no accompanying writing, because I don't really have an actual point I'm making. It's just a mildly annoying observation I've made and then wrote a macro to mostly resolve it, and I explicitly am not asking some "official solution" (something new Lispers are prone to do). But I guess I'll post it anyways because I don't think I've seen anyone talk about it. *** Something I've observed about Lisp is that if you have a lot of bindings, it pushes your code wayyyy to the right and it's kinda annoying. Especially if you're like me and like using some fancy features like multiple return values (which are very inconvenient without wrapper macros in every Lisp dialect for some reason) or such. It becomes even more of a problem if you like generally following an “Inliniac”[1] strategy, which I usually tend to do when writing Lisp in particular; since then having complex expressions in a binding get pushed even further right than the body of the binding. I often find that the actual bulk of the work in code ends up in the binding section, with the body just being some cleanup or other miscellaneous auxiliary tasks using the results of the main operation. For instance, take these completely arbitrary and nonsensical pseudocode samples: int a = 1; int b = a * 2; int *slot = &object.slot; int c, d = returnsMultipleValues(a, b); int e(int x, int y) { if x <= 0 { return y; } else { return e(x-1, y+1); } } int f = a * d * e(c, *slot); // at the exact same level we were at before print(a, b, slot, c, d, f); // ... (let* ((a 1) (b (* a 2))) (with-accessors ((slot slot)) *object* (multiple-value-bind (c d) (returns-multiple-values a b) (labels ((e (x y) (if (<= x 0) y (e (1- x) (1+ y))))) (let ((f (* a d (e c slot)))) ;; now indented 5 levels, and all the expressions in the binding sections ;; above end up even further right, annoying if you want a longer ;; expression in them, which is often desirable in Lisp (format t "~@{~a ~}" a b slot c d f) #| ... |#))))) The Lisp example ends up with the binding body thrown way off to the right, which then can make you wrap the body code a lot more if you generally stick to a line length limit. But almost as importantly, the binding sections of the innermost `LET` or the `LABELS` are even further to the right than the inner body code, making lambdas or nontrivial expressions that you need to save the result value of really horrible to format in a nice way. But, it's a bit silly for me to complain about because quite a while ago I wrote my own binding macros[2] that alleviate this almost completely. It also achieves making using multiple values just as convenient as normal single values which should be a built- in feature of all Lisps IMO.[3] (bind* ((a 1) (b 2) (:accessors (slot) *object*) (c d (returns-multiple-values a b)) (:λ e (x y) (if (<= x 0) y (e (1- x) (1+ y)))) (f (* a d (e c slot)))) ;; now only indented one level. only helps a little bit with the issue of ;; wanting a longer expression in the binding section though (format t "~@{~a ~}" a b slot c d f) #| ... |#) I'd *like* to imagine that any Lisper ends up with some binding macro that alleviates a lot of the pain. I know of a few others from very very experienced Common Lispers (e.g. the former is from Ron Garret, famous for pushing NASA to use Lisp): Ron Garret's BINDING-BLOCK: https://github.com/rongarret/ergolib/blob/master/core/binding-block.lisp Scott L. Burson's NEW-LET: https://gitlab.common-lisp.net/misc-extensions/misc-extensions/-/blob/master/src/new-let.lisp And for other Lisps, Sandra Snan/Idiomdrottning has a custom `define` in their Brev library for Scheme: Brev for Chicken Scheme: //idiomdrottning.org/brev But then, I couched the above with "I'd *like* to imagine", because in all the code I've read, it's extremely rare to actually see custom binding macros. Like, all the ones I linked above are just people's personal sets of utilities used only in their own programs rather than commonly used libraries, and I haven't seen any commonly used libraries that include anything like them. I guess it could be the stereotypical Lisp Curse[4] of everyone making their own, so none become de facto standards like Alexandria did for some Common Lisp utilities. But I guess it could also be that it's solely me and a handful of other people that see this as an actual problem/annoyance to solve? [1]: //idiomdrottning.org/inliniac [2]: https://git.sr.ht/~nytpu/nytpu.lisp-utils/tree/master/bind.lisp#L314-451 [3]: Not like Common Lisp doesn't have `SETF` that does tons of macro magic bullshit to transform mutation to lower-level forms, why shouldn't there be a wrapper around the lower-level `MULTIPLE- VALUE-BIND`? [4]: http://www.winestockwebdesign.com/Essays/Lisp_Curse.html *** Semi-related: it's probably brainworms from writing Lisp, but in C- like languages I have picked up the habit of (in my own projects) introducing blocks to scope variables instead of letting unused variables hang around until the end of the function. I guess it could be argued that if a function is long enough to need that then you should split it up into multiple functions, but also sometimes things in the real world just gotta be long and it doesn't make much sense to split it up. For example: // ... { int value = getValue(); // ... } // value isn't needed here // ... * * * Contact via email: alex [at] nytpu.com or through anywhere else I'm at: gopher://nytpu.com/0/about Copyright (c) 2025 nytpu - CC BY-SA 4.0