Hacker News new | ask | show | jobs
by TeMPOraL 1069 days ago
Arguably, the ideal here would be to abandon our obsession with directly operating on the plaintext source code that is a single, canonical source of truth.

Whether the code is split into 100 little helper functions, or is just one long block of the same code inlined into a single function, or somewhere in between - those are view issues. They do not have - or at least should not have - any implication on semantics. It makes zero sense to try and commit to some one perfect balance, because at any given moment, I may want to have everything inlined, or everything extracted to functions, or everything inlined down to two levels deep in the call stack, etc. It all depends on the reason I'm looking at that piece of code in the first place.

This is one of those holy wars that can't be won, because the problem is the limitation of the medium. Similarly with increasingly arcane ways of lazily flatmapping monoids across sets of endofunctors, all to avoid "callback hell", hidden state, non-local control flow, incomplete type definitions, etc. - all at the same time. You just can't do that - the reason bleeding-edge PLs get increasingly weird with syntax and require math degree to grok ideas behind them, is because they aren't improving things - they're just sliding along the Pareto frontier[0], hitting the boundary of plaintext capacity. There's only so many cross-cutting concerns you can express simultaneously in the same piece of text, without making it impossible to understand. And the irony about cross-cutting concerns is, at any given time you want to ignore most of them.

What I mean is, say I'm reading your 100-helper-methods "clean code" function, trying to fix a tricky bug in the algorithm it's implementing. The helper methods are an annoyance, I want them all inlined (with properly interpolated parameter names - i.e. the exact inverse of "extract method"). Also, unless I have a reason to suspect the bug is related to error handling, I don't want to read any of your fancy Result<T, E> noise you use in lieu of exception handling. Hell, I don't want to see exceptions either - I want all error handling logic to disappear entirely (or be replaced with a bomb emoji, so I remember it's there). Same with logging/instrumentation, and a bunch of async calls and the whole "color of your function" bullshit. Not relevant to my problem, I don't care, I don't want to see this. Hell, I probably don't care about most types involved either.

Next week, I'm back to the same code, trying to improve some logging and fix the case where error information isn't propagated. Suddenly, I now want to have those 100 helper functions be their own named things. I want to see Result<T, E> - in fact, I probably don't want to see the T part. Logging? Yes. Async? Probably still no.

A month from now, the impossible happens, and I'm given time to optimize performance of that same piece of code. Obviously, this calls for different set of readability tradeoffs (and more than anything, the ability to change them in flight).

So how about we stop wasting all our combined brainpower on arguing in circles over fake problems, which exist only because we insist on only ever working with the one, single, canonical plaintext representation of a program? It's literally the software equivalent of one-size-fits-all shoes - no matter which size you pick, the fit will be bad for almost everyone. Exceptions vs. Result<T, E>, or "lots of small functions" vs. "few big functions", etc. are all just different ways of asking which shoe size is the bestest shoe size - the problem isn't the shoe size, but that we have to pick a single size for everybody.

--

[0] - https://en.wikipedia.org/wiki/Pareto_front

3 comments

I can't fault your diagnosis of the problem. Fixing it would be... ambitious, though.

But maybe it would be easier in some languages than others. Lisp, being essentially a naked syntax tree, might be amenable to this. And Go has automated formatting tools. Granted, they aren't used for this, but perhaps they could be extended to do so.

But doing this for something like C++? That seems like something that's going to take a long time.

Fixing this is not a language-level problem, it's a tooling-level problem (and, of course, a big philosophical/cultural problem).

For an incremental approach, we could treat C++, or Python, or Lisp, as they are today, as serializing formats. We can keep using the existing process of compiling source code into executables - what matters is that we stop writing that code directly. That doesn't mean write no code at all, but rather breaking up with the idea that what we write the exact text files that then get compiled into programs.

Instead, the way forward is to treat the source code as more of a database, an artifact we sculpt (and yes, eventually it would be beneficial to ditch the plaintext source file representation too). This means the same underlying source code can be viewed and edited in many ways, textual and graphical, tailored to help with specific concerns or abstraction levels. So basically, take the modern Java/C# IDE, but instead of treating its advanced functionality as something to save you some typing, embrace them as the main way of looking at and modifying code. Then take it up to 11.

There's a precedent to that, too. It's not Lisp though - it's Smalltalk.

Ah, that's an interesting perspective.

I'll postulate a core goal, with regards specifically to optimal abstraction/inlining, involves ensuring the functional dependencies mirrors the readers internal conceptual representation of the problem domain as closely as possible. I consider this property as part of legibility and more important than readability. What it sounds like you are saying is that different tasks involve different conceptual representations and thus need different views, and I find that reasonable.

I'm not convinced that this is possible right now - if at all, however. The crux of the problem is that extraction supporting more than the abstractions you already implemented would encounter obstacles. I suspect automatically abstracting would be a hard problem. And the function naming problem would re-emerge - both you could give functions wrong names, and also your system could. In some cases there will not even be such a thing as an acceptable name, unless you assign random strings, in which case you'll have to check the abstracted code anyway to see what it does. And type signatures/property-based tests can't save you here, because the system would have to determine those. So automatically acquiring different views of the code may not be feasible.

Maintaining multiple views of manually implemented function hierarchies over the same data also sounds even more prone to name/doc drift than conventional formulations.

My point here is that for extraction to work, you have to implement the code you are working on in terms of all the helper functions already and you'd still have to deal with how you chose abstractions and names and description drift and so on.

Your concrete suggestions seem to be to use an IDE that can inline and fold code, use a binary file format for code, use auto-formatting tools, and give your language multiple ways to do each thing so you can have the IDE reformat a block of code with a different style to change how is presented. The only new feature may be using a ML model to tag code according to purpose so you can hide code by tag or hide all code not matching a tag.

While I agree those could be useful, we'd still end up with a canonical lexical function dependency hierarchy to obsess over. The problem of inlining vs abstracting still comes up there. Now abandoning that would be something, but I don't know how to do it.

I basically 100% agree with this and this is the editor env I wish I could live in.

I think the debate still exists because editors/langs don't work like this and you end up having to compromise one way or another. Obviously, if we could do really fancy code folding / inlining / condensing based on arbitrary user defined filters everyone would just write whatever they wanted.

I guess code review might be the only place where people would still complain, depending on how that looked.