Live data from Hacker News

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

seeinglogic.com

161–170 of 383 posts

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

#161

Earlier quoted context omitted.

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.

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 standard approach to complexity like that is to invent useful concepts and give them descriptive names. Then you can reason about the concepts themselves, without having to consider the steps you used to reach them.

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

#162
post #115

Earlier quoted context omitted.

authors_of_long_books = set() for book in books: if len(book.pages) > 1000: authors_of_long_books.add(book.author) return authors_of_long_books You are told explicitly at the beginning what the type of the result will be, you see that it's a single pass over books and that we're matching based on page count. There are no intermediate results to think about and no function call overhead. When you read it out loud it's…

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.

Set comprehension are more idiomatic here (explicit syntax) though filter/map are not that bad too:

    {*map(_.author, filter(_.page_count > 1000, books))}
It uses lambdas package.

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

#164

Earlier quoted context omitted.

That's not true. The lambdas used in the functional version are each called once for every item in the list.

No sane optimizer is going to emit the functional code as a gajillion function calls.

True, but now you're relying on a specific implementation and optimization of the compiler, unless the language semantics explicitly say that lambdas will be inlined.

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

#165

Earlier quoted context omitted.

That's not true. The lambdas used in the functional version are each called once for every item in the list.

No sane optimizer is going to emit the functional code as a gajillion function calls.

Yeah, if you treat it as javascript vs python they're likely correct (I'm not that familiar with js). The article and original comment were about function vs imperative though, so I assumed half decent runtimes for both.

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

#166

Earlier quoted context omitted.

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.

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.

That's only true for casual reviewing and writing.

When you're actually analyzing a bug, or need to add a new feature to the code... Then you'll have to keep the whole thing in your mind. No way around it

It gets extra annoying when people have complex maps, reduces, flat maps all chained after the next, and each step moved into a named function.

HF constantly jumping around trying to rationalize why something happens with such code...

It looks good on first glance, but it inevitably becomes a dumpster fire as soon as you need to actually interact with the code.

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

#167
post #54

Earlier quoted context omitted.

I 100% agree with this. One of the best compliments I ever got (regarding programming) was from one of my principal engineers who said something along the lines of "your code reads like a story". He meant he could open a code file I had written, read from top to bottom and follow the 'narrative' in an easy way, because of how I'd ordered functions, but also how I created declarative implementations that would 'talk'…

> The functions are self contained in that their dependencies/inputs are the arguments provided or other pure functions and the outputs are entirely in the return type. Is this just a fancy way of saying static functions?

Nope, pure functions are referentially transparent. The key idea is that you can replace the function invocation with a value and it shouldn’t change the program.

A regular static function could refer to a file, a database, or it could change some global memory, etc. So, replacing the static function (that causes side-effects) with a pure value wouldn’t result in the same program.

Side-effects are usually declaratively represented by something like an IO monad. Which in reality is just a lambda with the side-effecting behaviour in the body of the lambda.

So, to make a pure IO function you don’t actually perform the IO in the function, you return a data type (the lambda) that represents the IO to perform. This maintains the purity if the function and ‘passes the buck’ to the caller. In the case of Haskell, all the way up to its Main function and into its runtime — making the language itself pure, even if the runtime isn’t.

This isn't just a Haskell thing though. I'll write code this way in C# (and have built a large pure-FP framework for C# to facilitate this approach [1]).

Here's an example of the more 'narrative style' [2] of C# using pure-FP. It reads from top-to-bottom, keeping the related functions near each other and walking the reader through the functionality. There's also a massive removal of the usual clutter you see in C#/Java programs, getting down to the essence of the logic. It won't be to everybody's taste (as it's not idiomatic at all), but it demonstrates the idea.

This style works well for regular program logic and less well for things like APIs where there's not always a narrative you can tell.

[1] https://github.com/louthy/language-ext

[2] https://github.com/louthy/language-ext/blob/main/Samples/Car...

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

#168
post #130

This is interesting. Something I long wondered about Lisp code, was how having glyphs that are angled (parenthesis) without much indentation in many cases might be difficult to read just because of the visual aspects of it. ((( (( ( Takes some staring at to figure out what's what.

Lisp code is an AST. Once that's internalized, the parenthesis fade in the background. Mentally, instead of editing code, you're just arranging the branches. So, when reading, you can usually ignore whole sections as they will evaluate to a single value (side effects are possible, but strongly discouraged)

Having 4 spaces as indentation helps people tease out where the branches even are in languages like C or Python or whatever, rather than the 1 or 2 that you see with a lot of Lisp. And those angled parents make lining things up vertically a teeny bit more difficult.

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

#169

Earlier quoted context omitted.

No sane optimizer is going to emit the functional code as a gajillion function calls.

True, but now you're relying on a specific implementation and optimization of the compiler, unless the language semantics explicitly say that lambdas will be inlined.

This is true of literally anything and everything your compiler emits. In practice the functional style is much easier to optimize to a far greater degree than the imperative style.

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

#170

I think things like Halstead complexity or cyclomatic complexity are more heuristic than law. To read code, the most important thing to me is the abstractions that are built, and how effectively they bury irrelevant complexities and convey important concepts. As an example, I recently refactored some Java code that was calling a service that returned a list of Things, but it was paged: You might have to make multiple…

That is exactly what is discussed in: https://www.goodreads.com/book/show/39996759-a-philosophy-of...

Man, this is the third reference to this book I am seeing this week, I need to order this book.
Post reply on HN