Live data from Hacker News

What makes code hard to read: Visual patterns of complexity (2023)

seeinglogic.com

181–190 of 383 posts

Re: What makes code hard to read: Visual patterns of complexity (2023)

#181
post #109

> Chaining together map/reduce/filter and other functional programming constructs (lambdas, iterators, comprehensions) may be concise, but long/multiple chains hurt readability This is not at all implied by anything else in the article. This feels like a common "I'm unfamiliar with it so it's bad" gripe that the author just sneaked in. Once you become a little familiar with it, it's usually far easier to both read an…

This comment seems unnecessarily mean-spirited... perhaps I just feel that way because I'm the person on the other end of it!

I agree the code you have there is very readable, but it's not really an example of what that sentence you quoted is referencing... However I didn't spell out exactly what I meant, so please allow me to clarify.

For me, roughly 5 calls in a chain is where things begin to become harder to read, which is the length of the example I used.

For the meaning of "multiple", I intended that to mean if there are nested chains or if the type being operated on changes, that can slow down the rate of reading for me.

Functional programming constructs can be very elegant, but it's possible to go overboard :)

Re: What makes code hard to read: Visual patterns of complexity (2023)

#182
post #57
post #21

Towards the end he had an example of splitting a sequence of "graph.nodes(`node[name = ${name}]`).connected().nodes().not('.hidden').data('name');" adding variable between some of the . in there and claimed it was marginally less efficient. This is sometimes true, but if it ever is you need to talk to your tool vendors about a better optimizes. If you are working in a language without an optimizer than the marginal d…

I found this example troubling because once all the line noise is added, first-op second-op third-op fourth-op fifth-op sixth-op feels so much more impenetrable than - first-op - second-op - third-op - fourth-op - fifth-op - sixth-op The point of functional styles isn't purely brevity (as implied by the commentary around this example), it also puts a focus on the clear sequence of operations and helps reduce "operato…

I do both. It depends on whether I can think of a concise variable name that faithfully describes the intermediate result. If you need more than 20-ish characters to describe it, then it is better to leave it chained.

Re: What makes code hard to read: Visual patterns of complexity (2023)

#183
post #159
post #109

> Chaining together map/reduce/filter and other functional programming constructs (lambdas, iterators, comprehensions) may be concise, but long/multiple chains hurt readability This is not at all implied by anything else in the article. This feels like a common "I'm unfamiliar with it so it's bad" gripe that the author just sneaked in. Once you become a little familiar with it, it's usually far easier to both read an…

That's not a long chain. It doesn't even have a reduce, try nesting a few reducers and see how you like it.

What is "long"?

Re: What makes code hard to read: Visual patterns of complexity (2023)

#184
post #61

My pet peeve: function getOddness4(n: number): if (n % 2 === 0): return "Even"; return "Odd"; While it is shorter, I prefer vastly prefer this one: function getOddness2(n: number): if (n % 2 === 0): return "Even"; else: return "Odd"; Reason: getOddness4 gives some sense of asymmetry, whereas "Even" and "Odd" are symmetric choices. getOddness2 is in that respect straightforward.

function getOddness(n: number): return (n % 2 === 0) ? "Even" : "Odd"; Lowest boilerplate makes it the most readable. If working in a language with the ternary operator it ought to be easily recognized!

This is, IMHO, the idiomatic way to do so.

Re: What makes code hard to read: Visual patterns of complexity (2023)

#185

Earlier quoted context omitted.

In the example above, you first have a list of books. Then you filter it down to books with >1000 pages. Then you map it to authors of books with >1000 pages. Then you collapse it to distinct authors of books with >1000 pages. Every step in the chain adds further complexity to the description of the things you have, until it exceeds the capacity of your working memory. Then you can no longer reason about it. The stan…

Folks who are familiar with chaining don't think about it in the way that you've presented. If you're familiar, it's more like: Filter to the books with >1000 pages Then their authors. Finally, distinguish those authors. If you're familiar, you don't mentally represent each link in the chain as the totality of everything that came before it _plus_ whatever operation you're doing now. You consider each link in the cha…

The problem with that is that it's all implicit. If the steps are sufficiently complex and if you don't already know what the code is doing, you don't always have a clear mental image of what the intermediate state after each step is supposed to represent. And with a chained syntax like that, you don't have an option to give the intermediate state an explicit name. A name that could help the reader understand what is going on.

You don't have to give a name to every intermediate state, just like you don't have to comment every single line of code. But sometimes the names and comments do improve readability.

Re: What makes code hard to read: Visual patterns of complexity (2023)

#186

Earlier quoted context omitted.

You don't ever need to "understand everything at once". You can read each stanza linearly. The for loop style is the approach where everything often needs to be understood all at once since the logic is interspersed throughout the entire body.

In the example above, you first have a list of books. Then you filter it down to books with >1000 pages. Then you map it to authors of books with >1000 pages. Then you collapse it to distinct authors of books with >1000 pages. Every step in the chain adds further complexity to the description of the things you have, until it exceeds the capacity of your working memory. Then you can no longer reason about it. The stan…

You have literally just described the set of objects asked for: the unique authors of the books with more than 1,000 pages. I don't understand how you expect to get any simpler than that. The functional style isn't even requiring you to describe how to accomplish it, it almost verbatim simply describes the answer you're trying to get.

If your entire objection is that you might want intermediate-named variables… you can just do that?

    var longBooks       = books.filter(book => book.pageCount > 1000)
    var authors         = longBooks.map(book => book.author)
    var distinctAuthors = authors.distinct()
For short chains (95%+ of cases), this is far more mental overhead. For the remaining cases, you can just name the parts? I'm just completely failing to see your problem here.

Re: What makes code hard to read: Visual patterns of complexity (2023)

#187

Earlier quoted context omitted.

Folks who are familiar with chaining don't think about it in the way that you've presented. If you're familiar, it's more like: Filter to the books with >1000 pages Then their authors. Finally, distinguish those authors. If you're familiar, you don't mentally represent each link in the chain as the totality of everything that came before it _plus_ whatever operation you're doing now. You consider each link in the cha…

The problem with that is that it's all implicit. If the steps are sufficiently complex and if you don't already know what the code is doing, you don't always have a clear mental image of what the intermediate state after each step is supposed to represent. And with a chained syntax like that, you don't have an option to give the intermediate state an explicit name. A name that could help the reader understand what is…

So just do that then in the cases where you think it improves clarity? It's not like you can't assign names in the functional style if you need to.

Re: What makes code hard to read: Visual patterns of complexity (2023)

#188
post #109

> Chaining together map/reduce/filter and other functional programming constructs (lambdas, iterators, comprehensions) may be concise, but long/multiple chains hurt readability This is not at all implied by anything else in the article. This feels like a common "I'm unfamiliar with it so it's bad" gripe that the author just sneaked in. Once you become a little familiar with it, it's usually far easier to both read an…

Your example is a conceptually simple filter on a single list of items. But once the chain grows too long, the conditions become too complex, and there are too many lists/variables involved, it becomes impossible understand everything at once. In a procedural loop, you can assign an intermediate result to a variable. By giving it a name, you can forget the processing you have done so far and focus on the next steps.

If you assign an intermediate result to a variable in a procedural loop, you can also assign intermediate results of parts of this chain to variables.

Re: What makes code hard to read: Visual patterns of complexity (2023)

#189
post #67

> For long function chains or callbacks that stack up, breaking up the chain into smaller groups and using a well-named variable > Is the second one marginally less efficient? > Yes. No, both versions are just as efficient: In both versions, the same objects are allocated, stored on the heap, and garbage collected. The difference in efficiency comes down to the compiler. For the second version, the compiler should ob…

I agree. After compiling it is even quite likely that the compiler does not care you gave a name to a return value (assuming you let it infer the variable type). What you will see in practice is that the intermediate is explicitly “materialized” (e.g. into a list), because the author wanted to inspect it in the debugger. That will have some cost, mostly in the form of avoidable allocations.

Re: What makes code hard to read: Visual patterns of complexity (2023)

#190
post #129

Earlier quoted context omitted.

fwiw, once Python's introduced there's the third option on the table, comprehensions, which will also be suggested by linters to avoid lambdas: authors_of_long_books: set[Author] = {book.author for book in books if book.page_count > 1000} These are somewhat contentious as they can get overly complex, but for this case it should be small & clear enough for any Python programmer.

Without syntax highlighting, "book.author for book in books if book.page_count > 1000" requires a lot more effort to parse because white space like newlines is not being used to separate things out.

You've had some answers already, but I also think this is a good argument for syntax highlighting. With tools like tree-sitter it's pretty easy these days to get high quality syntax highlighting, which allows us humans to receive more information in parallel. A lot of the information we pick up in our daily lives is carried through color, and being colorblind is generally seen as a disability (albeit often a mild one which can be undetected for decades).

Syntax highlighting in print is more limited because of technological and economic constraints, which might leave just bold, italics and underlines on the table, while dropping color. On screens and especially in our editors where we see the most code, a lack of color is often a self-imposed limitation.

Post reply on HN