Live data from Hacker News

Avoid Indirection in Code

matthewrocklin.com

51–60 of 220 posts

Re: Avoid Indirection in Code

#51
post #40

It's not indirection , it's bad abstraction that's the real issue here. Consider the example used in the article: if x.startswith("foo"): do_something_with(x) if is_foolike(x): do_something_with(x) The problem with both of these variations is that the "if-statement" doesn't have any meaning behind it. There's no gain in the indirection presented here. Whereas the following code has meaning: if checkHasPermissions(x):…

I agree with you. I think it becomes pretty clear when applied to the example in the article of code review. If the abstractions are designed and named well, why should the reviewer need to jump to the declaration of the function in the if statement? Assuming the checkHasPermissions function wasn’t added or modified in the PR, the reviewer should just consider whether it makes sense to do_something if and only if checkHasPermissions.

Re: Avoid Indirection in Code

#52

There's a lot of reasons to avoid indirection but I don't think decreasing the number of files you have to navigate is one of the better ones. I mean, who reads code linearly anyway. My hand hovers over F12 (the shortcut for go to definition in Visual Studio) half the day. Any good IDE will help with this. I feel like this person is like, I don't want to drill all these holes to build my deck. I'll use nails. And it'…

I think also this idea of navigating files is wrong; shouldn't these functions, generally speaking, be in the same file?

Re: Avoid Indirection in Code

#53

Earlier quoted context omitted.

I'm sorry that this will come across as rude, but I feel like you're repeating something you've heard but didn't quite understand. Yes, at pretty much ANY level, you should write your function names to hide the implementation details and to describe the intention of the function. In my previous post I hinted that the name of the function should "describe what the function does", which is the same as "describe the int…

I'm sorry that this will come across as rude, but I feel like you're repeating something you've heard but didn't quite understand. You're right, it does come across as both rude and condescending, and you know it, so don't apologise. In my previous post I hinted that the name of the function should "describe what the function does", which is the same as "describe the intention of the function", because if the functio…

> `is_foolike` to me, implies `test_for_abstract_quality_foo`.

Yes, which is why when `is_foolike` tests that a string starts with 'foo', that's rather unexpected. If `is_foolike` actually describes the intention of the function, then the function testing for the string beginning with 'foo' is a bug, because it doesn't do what it's intended to do.

Referring to "the quality of starting with 'foo'" as "abstract quality foo" isn't hiding implementation details, it's being opaque about what a function does.

Put another way, "what a function does" isn't its implementation. "How a function does what it does" is its implementation.

> `starts_with_foo` implies an assertion a string beginning with a particular prefix

Yes, exactly. Because that is what it is intended to do, we hope, since that's what it does. That's NOT the implementation: there are plenty of different ways to implement testing whether a string starts with 'foo', and the name `starts_with_foo` isn't coupled to any of them.

> The point being that even though, in this (again) entirely fictitious example, the intention of the method is different from it's implementation

Yes. `starts_with_foo` is also different from the implementation, while still describing the intention of the method.

Could you explain to me why you think `starts_with_foo(x)` describes the implementation `x.startswith('foo')` and not some other implementation (such as `x[:3] == 'foo'`)?

Re: Avoid Indirection in Code

#54
I don’t quite get the point of article. It essentially boils down to:

“Abstractions — or rather, indirections — are bad for for readability, but they are good in some cases.”

Umm... this can be said for pretty much anything. Over use of anything is bad. Inappropriate use of anything is bad.

> Mostly, I want authors to be aware that there is a human cost to indirection that is felt more acutely by everyone reading the code except the original author.

Sure. It’s a well known fact that abstractions have associated costs — nothing new there. However, the author seems to forget that not abstracting things can be equally costly — even when human readability is concerned.

Imagine someone trying to figure our the business logic be reading code. All they need to know is some conditions are being met for certain steps to be executed. The actual steps involved in the check are irrelevant to the business logic. The condition can be modelled separately from the business logic and can even change independently. For example:

    def is_foo_condition_satisfied((self):
        return self.variable.startswith(‘foo’)
In this case, it just happens that the satisfaction of a condition is implemented as prefix check. In the future, it might be changed to checking a flag or reading a database, or anything really. If you sprinkle the startswith() check all over you code, it will be hard to make the change. And it actually hurts readability. While reading a large codebase, you want to focus on specific parts of it — getting into the details of everything all at once does not make it easier.

The most important thing to note is — programming is not confined to s strict set of rules. It’s not an exact science. You can’t define a strict a rules that must be followed. What makes sense somewhere is completely useless elsewhere. The Linux Kernel is full of goto statements, despite the fact that goto is considered harmful.

Dogma Driven Programming must be avoided. Even well known programming practices are guidelines at best. One must evaluate whether a given rule fits a given situation.

Coming back to the example at hand, functions are meant to divide the program by the logical tasks being performed, not by number of lines or any other metric. As I said before, it’s more art than exact science.

Is the check for foolikeness of something a logically distinct task? It depends on the context. There is no way to write a cardinal rule either way.

PS. I sincerely hope no one comes to me for code review with a monolithic function because of all the inlining — citing this article as inspiration.

Edit: Typo correction.

Re: Avoid Indirection in Code

#55
post #40

It's not indirection , it's bad abstraction that's the real issue here. Consider the example used in the article: if x.startswith("foo"): do_something_with(x) if is_foolike(x): do_something_with(x) The problem with both of these variations is that the "if-statement" doesn't have any meaning behind it. There's no gain in the indirection presented here. Whereas the following code has meaning: if checkHasPermissions(x):…

Even better (IMO), "hasPermissions"

It isn't clear whether the "check" prefix means the function returns based on whether the checking was successful or not.

Re: Avoid Indirection in Code

#56
The point made by the article (avoid using due to problems for debugging and code review etc.) does not hold much water. 'Indirection' can be invaluable while separating 'how you got it' from 'what to do with it' (it=some data), for example. 'what you do with it' can remain unaffected by changing 'how you got it'. This will not mess with code review practices but make understanding the pieces easier. It will also make debugging easier as you can test both those pieces independently. What the OP calls indirection is just bad code or meaningless abstraction.

Zed Shaw puts it well: "Abstraction and indirection are very different yet cooperating concepts with two completely different purposes in software development. Abstraction is used to reduce complexity. Indirection is used to reduce coupling or dependence."

Re: Avoid Indirection in Code

#57
See also Zed Shaw's "Indirection Is Not Abstraction". The blog post is a bit overlong, but Shaw's point is important:

Abstraction and indirection are very different yet cooperating concepts with two completely different purposes in software development. Abstraction is used to reduce complexity. Indirection is used to reduce coupling or dependence.

https://zedshaw.com/archive/indirection-is-not-abstraction/

Re: Avoid Indirection in Code

#58
post #56

The point made by the article (avoid using due to problems for debugging and code review etc.) does not hold much water. 'Indirection' can be invaluable while separating 'how you got it' from 'what to do with it' (it=some data), for example. 'what you do with it' can remain unaffected by changing 'how you got it'. This will not mess with code review practices but make understanding the pieces easier. It will also mak…

Yup, seems like the author had trouble following badly written abstraction and/or indirection and came up with the idea that they are bad.

Abstractions are invaluable if you are dealing with a large codebase. Even the original author can’t keep everything in their head.

Indirections are invaluable if you want to be able to modify code in the future without making a gigantic mess — and potentially breaking compatibility in the process.

Re: Avoid Indirection in Code

#59
post #22
post #13

The argument in the post is a bit of a strawman - for really simple examples like that indirection makes no sense. Saying "is_something_like" as an alias for "starts_with" is an unnecessary alias that does nothing for the readability or duplication of the code. Indirection really isn't necessary in this case, even if you use "starts_with" in a thousand places. It might make sense if you're trying to do a comparator,…

In Python it's json.load(file), so no indirection needed whatsoever.

It doesn't stop being what it is just because the standard library does it.

Re: Avoid Indirection in Code

#60
It’s not just a problem for review and debugging. I think it’s even worse for refactoring and new feature development, because it makes it so much more difficult to know where to make a change. If the condition in the (silly) example needs to change, should you write a new function to replace the call to is_foolike, should you rewrite its implementation, or should you add a parameter that alters its behavior?
Post reply on HN