Live data from Hacker News

Linus Torvalds' good taste argument for linked lists, explained

github.com

61–70 of 339 posts

Re: Linus Torvalds' good taste argument for linked lists, explained

#61
I've seen this presentation, and if I remember correctly, this example was more an attempt to convey what good taste can relate to, more than a definitive answer on whether the second is actually better than the first.

Many comments here are arguing that the first answer is actually better because it is clearer. I think it feels clearer to many of us not because it is actually simpler, but because it is the one we learnt at school / are used to see and therefore know by heart.

Linus is probably aware of this and may have meant to surprise the public with the second solution.

Why the second solution is better? Not because it does less branching and is more efficient. This misses the point. Not because there are fewer lines of codes and is terser. This also misses the point.

Fewer special cases means less ways to screw up, and also easier to follow. In the general case. Not only in this specific linked list example. And also clearer code.

It's just that in this specific case, we are used to the first solution that we are able to recognize at a glance (we "pattern-match" it).

Do you remember when you had to grasp this first solution the first time you encountered it or tried to write it? Many of us probably screwed it up and wasted time making it work. We might have forgotten the exact edge case Linus Torvalds was pointing out in this presentation. At least for me, I remember it was hard. I probably would have had easier time understanding the second solution by the way. The difficulty is a pointer indirection, but you better really understand pointers correctly when you are manipulating linked lists in C anyway.

Comments here also speak about leaving maintainable code for future developers on the project and avoid clever solutions to make their life easier, but it is the whole point of the second solution: let them not have to think about edge cases as much as possible.

Don't stop on this linked list example. We are all used to the first solution and Linus Torvalds probably picked this example because many people know linked lists. The message is: fewer edge cases is better. The goal is not to be "clever", in the negative sense.

Also see the original code with comments, which is way more readable: https://news.ycombinator.com/item?id=25327066

Re: Linus Torvalds' good taste argument for linked lists, explained

#62

I'm not sure why the article removed comments from the code and replaced variable names like "indirect" with "p". Here are the two code samples verbatim from Linus's presentation: remove_list_entry(entry) { prev = NULL; walk = head; // Walk the list while (walk != entry) { prev = walk; walk = walk->next; } // Remove the entry by updating the // head or the previous entry if (!prev) head = entry->next; else prev->next…

Perfect. I almost commented to complain that the “elegant” version is harder to read and understand. This is much much better. Comments are important!

Re: Linus Torvalds' good taste argument for linked lists, explained

#63
> [...] I don't want you to understand why it doesn't have the if statement. But I want you to understand that sometimes you can see a problem in a different way and rewrite it so that a special case goes away and becomes the normal case, and that's good code. [...] -- L. Torvalds

I think the idea of this extends much beyond a linked-list implementation, into software design and architecture.

Sometimes, you find more elegant solutions to something, that inherently do away with edge cases. I think this is the original intent, to show that you can find beauty, much as chessplayers do in chess. These solutions may be harder to understand completely, but you can actually encapsulate them in a function or use them as patterns!

A point is also made, there's often a rewrite involved. You usually don't need to find this stuff on first try.

Re: Linus Torvalds' good taste argument for linked lists, explained

#64
post #50

I understand the general point (and value) of reframing the problem or the solution in a way that removes special cases ... but in this case I would actually prefer the first solution over the second. The second solutions reminds me of the old-school perl culture, and JavaScript culture, where 'cleverness' (which always manifests itself as terseness as if lines of code were expensive), takes precedence over maintaina…

Fever lines aren't automatically better, but fewer lines means less code to maintain. More often than not, fewer lines is easier to read too. The example here is a good one. The shorter version reads much better and is more obviously right.

I have one to bring up "for loops".

I think they are infinitely worse than while loops. (I ran into this specifically with Pandas dataframes/series)

You save 1 or 2 lines and have ambiguity on wherever "each" is.

Maybe static typing solves this, but I've decided I hate syntax sugar and dynamic typing.

Re: Linus Torvalds' good taste argument for linked lists, explained

#65

I understand the general point (and value) of reframing the problem or the solution in a way that removes special cases ... but in this case I would actually prefer the first solution over the second. The second solutions reminds me of the old-school perl culture, and JavaScript culture, where 'cleverness' (which always manifests itself as terseness as if lines of code were expensive), takes precedence over maintaina…

There's arguments to be made on both sides, but I think the problem with Linus's solution here is that it doesn't quite clearly establish the assumption being made, which gives it a bit too much of the 'cleverness' flavor that you allude to. A better implementation would be one that does establish why the use of pointers make sense:

   void remove_entry(node_t *entry) {
     // curr_ref is the address of the link pointing to curr
     node_t **curr_ref = &head;
     node_t *curr = *curr_ref;
     while (curr != NULL && curr != entry) {
       // Advance curr_ref and curr
       curr_ref = &curr->next;
       curr = *curr_ref;
     }
     if (curr) { *curr_ref = curr->next; }
   }
Choosing names somewhat more rationally, and making it clear that "curr" always points to the current node, and that "curr_ref" is the address of whatever pointer we followed to arrive at curr, makes it easier to establish the invariant that updating curr_ref is sufficient to insert or remove curr into a list, no matter if it's referring to the head link of a list or the next link of the prior node.

Re: Linus Torvalds' good taste argument for linked lists, explained

#66

I understand the general point (and value) of reframing the problem or the solution in a way that removes special cases ... but in this case I would actually prefer the first solution over the second. The second solutions reminds me of the old-school perl culture, and JavaScript culture, where 'cleverness' (which always manifests itself as terseness as if lines of code were expensive), takes precedence over maintaina…

I totally agree, it’s about proving you’re clever rather than communicating ideas. Maybe for something hard like Linux kernel development this is a good thing but in most cases it just leads to messy code that people can’t understand or follow.

What if someone only ever learns what looks to be too clever to you?

You make it sound as if some universal utilitarianism exists.

Everyone’s personal event log is going to look just different enough to make that unreasonable.

Re: Linus Torvalds' good taste argument for linked lists, explained

#67

I understand the general point (and value) of reframing the problem or the solution in a way that removes special cases ... but in this case I would actually prefer the first solution over the second. The second solutions reminds me of the old-school perl culture, and JavaScript culture, where 'cleverness' (which always manifests itself as terseness as if lines of code were expensive), takes precedence over maintaina…

I completely disagree. Lines of code, long variable names, and meaningless hierarchies are some tasteless taxes. Some people like each of them(!), and I don't get it.

Why are long variable names a tax when there are such good auto completion tools? I get short variable names in algorithm code but in business logic long variable names can make code vastly more self-documenting.

Re: Linus Torvalds' good taste argument for linked lists, explained

#68
Pointless and bad article.

I compiled the presented code out curiosity on arm gcc 8.2 on godbolt.org with -O3 op and the results are:

(the original remove is even faster then the so called elegant or the elegant with inline)

  remove_cs101:
          ldr     r2, [r0]
          cmp     r2, r1
          bne     .L3
          b       .L9
  .L6:
          mov     r2, r3
  .L3:
          ldr     r3, [r2, #4]
          cmp     r1, r3
          bne     .L6
          ldr     r3, [r1, #4]
          str     r3, [r2, #4]
          bx      lr
  .L9:
          ldr     r3, [r2, #4]
          str     r3, [r0]
          bx      lr
  
  
  remove_elegant:
          ldr     r2, [r0]
          cmp     r1, r2
          bne     .L12
          b       .L13
  .L14:
          mov     r2, r3
  .L12:
          ldr     r3, [r2, #4]
          cmp     r1, r3
          bne     .L14
          add     r0, r2, #4
  .L13:
          ldr     r3, [r1, #4]
          str     r3, [r0]
          bx      lr
  
  remove_elegant_with_inline:
          ldr     r2, [r0]
          cmp     r1, r2
          cmpne   r2, #0
          bne     .L17
          b       .L18
  .L19:
          mov     r2, r3
  .L17:
          ldr     r3, [r2, #4]
          cmp     r1, r3
          cmpne   r3, #0
          bne     .L19
          add     r0, r2, #4
  .L18:
          ldr     r3, [r1, #4]
          str     r3, [r0]
          bx      lr

EDIT: Hmmm, so much downvote without any reply.

Well if your code needs an article to be explained, rather than the plain solution and brings nothing, even 1 instruction slower, as the compiler can't make the same (faster) result as the plain solution, than your code is pointless. And stating that this is the elegant solution is IMO bad practice.

Re: Linus Torvalds' good taste argument for linked lists, explained

#69
post #53

Earlier quoted context omitted.

Lines of code are expensive, because we expect human beings to reread them with almost no help from tools. Concise code is a temporary problem for novices, until they grow into experts. Bulky code is an ongoing problem for everyone and we don’t have a solution.

While I mostly agree with you, I think that use of double indirect pointers is rare enough that in effect each use of one probably counts as a "line of code" when trying to understand what's going on. The one Linus doesn't like likely runs faster (at a microarchitectural level), it's also the thing I've done for 40 years now, it's what comes out of my fingers when I code linked lists, for me at least it's more unders…

It's very unlikely it is faster.

Re: Linus Torvalds' good taste argument for linked lists, explained

#70
post #30

Maybe slightly off topic, but.. I don't think this is really about taste. It's about "common ground" between programmers. If you've seen and used the second pattern many times before, it becomes part of your vocabulary. You're then able to express yourself more succinctly. It is then obviously a better solution to you. If you see another programmer use the same pattern, it is common ground between you, and you like i…

The same argument was made recently about the reduce function: it's a known pattern for some and a hard to understand trick for others.

Yes, and you get used to reduce. map, filter and reduce were hard for me to follow… until I used them two or three times and now I understand them easily and they naturally come to my mind when I need to solve a problem which they can help solving.

They are good tools (especially in codebases shared with people who are allergic to loop statements!)

Post reply on HN