Live data from Hacker News

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

seeinglogic.com

331–340 of 383 posts

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

#331

Earlier quoted context omitted.

The problem is the "you" in question is not always able to. When "you" write code it makes sense and so you don't need to assign many names. The you in six months will want more names, and in 6 years that will be different again (how many depends - if this code is changed often then you know it much better than if it has been stable). The worse case will be after you "get hit by a bus" and the "you" in question is so…

Unlike the procedural approach, every step in a functional chain is wholly isolated and independent from the others. It is strictly easier to split this style of code up into two halves and name them than it is to disentangle procedural equivalents. I have quite literally zero times in my ~25 year career had to deal with some sort of completely inscrutable chain of functional calls on iterators. Zero. I am entirely c…

I will admit to not having written any significant functional code. However the poster child for functional programming always seems to be small programs (xmonad is the largest one I can think of, and the procedural counterparts are not that big either. Of course there is a lot of code out there that nobody can talk about). Thus I have to conclude the question of how that style scales to really large programs remains open.

That said, you didn't address my comment at all. It might be easier, but that doesn't mean it is easy to figure out what that long chain is really done - all too often the algorithm names don't tell you what you are really trying to accomplish in my experience.

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

#332
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.

The asymmetry is apparent if the code gets refactored to continuation/callback style or mutation of a more complex data structure, the first method will fall through and execute the second instruction set. Return is a special operator in this sense in that it breaks control flow and the ordinary control flow of the first method is not capturing the exhaustiveness of the two cases.

In idiomatic rust, return isn't used except for exceptional cases that break the control flow of the method and the second example is more commonly seen without return statements at all. Idiomatic python also typically early exits in the beginning with a return on invalid parameters or state with a tail position return being the usual actual return value. Because of these conventional practices, breaking the exhaustive if-else control structure makes the indented return appear exceptional (like an invalidity). If you follow these conventions than naturally the return statement begins to appear redundant except in the break-control-flow cases and the choice of the rust convention begins to make sense: in all languages return is a statement equivalent to break.

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

#333
post #240
post #157

Earlier quoted context omitted.

SELECT DISTINCT author FROM books WHERE pageCount > 1000;

Scrolled to find the SQL. Such an elegant, powerful language. Really happy I chose SQLite for my game/project.

Just add strong types, with verification at something-like-compile-time, and you'll have a sane language.

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

#334
> Let me be upfront: there is no commonly-used and accepted metric for code readability.

Indeed, I haven't found an autoformatter yet whose default settings I find "readable". The best of them is still clang-format, because it has so many parameters I can tune. By now I have a .clang-format config file that almost comes close to producing the formatting I do manually.

I also find functional patterns way more readable than their procedural counterparts.

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

#335
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…

When the filter/mapper becomes slightly more involved as it basically always is in real life code, the regular imperative approach is much nicer.

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

#336

Earlier quoted context omitted.

o_node := graph.GetNodeByName(name) var ret []string for _, node := range o_node.connectedNodes() { if !node.isHidden { ret = append(ret, node.name) } } return ret

There is just no way that reasonable people consider this to be clearer. One certainly might be more familiar with this approach, but it is less clear by a long shot. You've added a temp variable for the result, manual appending to that temp variable (which introduces a performance regression from having to periodically grow the array), loop variables, unused variables, multiple layers of nesting, and conditional log…

> performance regression

What? Golang append()s also periodically grow the slice.

> Conditional logic

it's just a single if, really, the same thing is there in your filter()

> Multiple layers of nesting

2... You're talking it up like it's a pyramid of hell. For what it's worth, I've seen way way more nesting in usual FP-style code, especially with formatting tools doing

  func(
     args
  )
For longer elements of the function chain.

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

#337

Earlier quoted context omitted.

Unlike the procedural approach, every step in a functional chain is wholly isolated and independent from the others. It is strictly easier to split this style of code up into two halves and name them than it is to disentangle procedural equivalents. I have quite literally zero times in my ~25 year career had to deal with some sort of completely inscrutable chain of functional calls on iterators. Zero. I am entirely c…

I will admit to not having written any significant functional code. However the poster child for functional programming always seems to be small programs (xmonad is the largest one I can think of, and the procedural counterparts are not that big either. Of course there is a lot of code out there that nobody can talk about). Thus I have to conclude the question of how that style scales to really large programs remains…

Ahh, it gets really interesting when you read code that does have named variables… and they’re misleading.

A strength of functional idioms is that they expose the structure of the code in a way that a name - even a well chosen name - can only hope to achieve. Often, succinctly and comprehensively. At that point you stop caring so much about variable names. They’re still there but you need them less

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

#338

Earlier quoted context omitted.

Everything you said is true for both of our programs, the only difference is whether or not it's hidden behind function calls you can't see and don't have access to. You don't really think that functional languages aren't appending things, using temp vars, and using conditional logic behind the scenes, do you? What do you think ".filter(node => !node.isHidden)" does? It's nothing but a for loop and a conditional by a…

> the only difference is whether or not it's hidden behind function calls you can't see and don't have access to. You "can't see and don't have access to" `if`, `range`, or `append` but somehow you don't find this a problem at all. I wonder why not? > You don't really think that functional languages aren't appending things, using temp vars, and using conditional logic behind the scenes, do you? By this metric all lan…

> by this metric

False equivalence. You're saying that the statement "both for.. append and .map() executing the _same steps_ in the _same order_ are the same" is equivalent to saying that "two statements being composed of cmp,jmp, etc (in totally different ways) are the same" That is a dishonest argument.

> Distinct could sort and look for consecutive, it could use a hashmap

People love happy theories like this, but find me one major implementation that does this. For example, here is the distinct() implementation in the most abstraction-happy language that I know - C#

https://github.com/dotnet/runtime/blob/main/src%2Flibraries%...

It unconditionally uses a hashset regardless of input.

Edit: found an example which does different things depending on input

https://github.com/php/php-src/blob/master/ext/standard/arra...

This does the usual hashset based approach only if the array has strings. Otherwise, it gets the comparator and does a sort | uniq. So, you get a needless O(nlogn), without having a way to distinct() by say, an `id` field in your objects. Very ergonomic...

On the other hand...

  seen := map[string]bool{}
  uniq := []string{}
  for _, s := range my_strings {
      if !seen[s] {
          uniq = append(uniq, s)
          seen[s] = true;
      }
  }
Let us say you want to refactor and store a struct instead of just a string. The code would change to...

  seen_ids := map[string]bool{}
  uniq := []MyObject{}
  for _, o := range my_objects {
      if !seen_ids[o.id] {
          uniq = append(uniq, o)
          seen_ids[o.id] = true;
      }
  }
Visually, it is basically the same, with clear visual messaging of what has changed. And as a bonus, it isn't incurring a random performance degradation.

Edit 2: An SO question for how to do array_unique for objects in php. Some very ergonomic choices there... https://stackoverflow.com/questions/2426557/array-unique-for...

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

#339
I have seen this subject come up as the most important factor of human behavior in every one of my employments. Counting both my corporate time and military time that is nearly 50 years employment time at more than 20 different organizations.

First things first, complex is a fancy word that means many. That's it, so don't over think it. Everything else is bias and opinions. If you feel conflicted about that see the fantastic Rich Hickey talk: Simple Made Easy https://www.youtube.com/watch?v=SxdOUGdseq4

Secondly, how this actually manifests is that people are eager to solve some problem in a way they feel comfortable, whether by process or automation, and then nobody wants to maintain anything, especially if it was written by somebody else. This is a critical failure.

The best solution I have found to solve for this is speed, not code style or anything else. Uncertainty increases proportionally to the duration between testable iterations. For example if it takes 30 seconds to try out an idea and reset back to the prior state people feel more certain about the environment in which they work because risk is low and learning is fast. If, on the other hand, it takes 90 minutes to try out an idea people will be exceedingly cautious about what they try and propose because everything is fragile and expensive.

Knowing that speed is the solution is not convincing in a world where most software developers cannot measure anything. Instead it must be mandated that software has only one purpose: automation, so automate the shit out of it. When that becomes the thesis of all work speed naturally self-amplifies and people split into two camps. The first camp is people that can actually write original software and the second camp is people who will kill you with internal processes, such as complexity checks and code style rules, because they cannot automate their way out of it.

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

#340

Earlier quoted context omitted.

> it could build up a set internally, it could use a hashmap, or any one of a million other approaches. It can even probe the size of the array to pick the performance-optimal approach. I don't have to care. Well, this is probably why functional programming doesn't see a lot of real use in production environments. Usually, you actually do have to care. Talk about noticing a performance regression because I was simply…

Your assumption that filter/map/reduce is necessarily slower than a carefully handcrafted loop is wrong, though. Rust supports these features as well and the performance is equivalent. Also countless real-world production environments run on Python, Ruby, JS etc, all of which are significantly slower than a compiled FP program using filter & map. > FP languages are dead-set on "immutability" which simply means creati…

> The compiler can make it mutable for better performance

Well, we already know that no pure FP language can match the performance of a dirty normal imperative language, except for Common Lisp (which I am happy to hear an explanation for how it manages to be much faster than the rest, maybe it's due to the for loops?). And another comment here already mentioned how those "significantly slower" scripting languages have a healthy dose of FP constructs -- which are normally considered anti-patterns, for good reason. The only language that competes in speed in Rust, which just so happens to let you have fast FP abstractions so long as you manually manage every piece of memory and its lifetime, constantly negotiating with the compiler in the process, thereby giving up any of the convenience benefits you actually get from FP.

Post reply on HN