Live data from Hacker News

Avoid Indirection in Code

matthewrocklin.com

171–180 of 220 posts

Re: Avoid Indirection in Code

#171

Err, this isn't bad advice, just kinda junior-level. First, if you're defining interface indirection to support encapsulation and you wind up never rewriting (or adding another) implementation behind that "external function" or method, you probably didn't need it. (YAGNI, yo?) Second, if you're defining interface indirection and it makes it harder to understand the code, you're doing it wrong anyway. - - - - Indirect…

well technically, if you type-annotate the dict you should be fine:

    dispatch: Mapping[str, Callable[arg_types, ret_type]] = {}

Re: Avoid Indirection in Code

#172
post #104

Earlier quoted context omitted.

Agreed, although I think the example isn't too bad. I often see such "helper methods" - usually in a class called "utils" or "misc" - when (novice) programmers "try to plan ahead": how should I write this so it can be re-used? But KISS is remains a good principle. Predicting the future is hard. If in doubt, don't put in the extra indirection/abstraction. If/when the next person needs that functionality, they can refa…

In my mind I contrast "keep it simple" with "make it simple". Successful "make it simple" is better than "keep it simple", which in turn is much better than failed "make it simple". Every attempt at "make it simple" is a wager. Regarding the one-caller method, beloved by followers of maximum method length ideology, scorned by enemies of excessive indirection, I think we lost a very useful tool when the "structured pr…

C# has local functions which I've been making a lot of use for this exact purpose.

Re: Avoid Indirection in Code

#173
post #125

Earlier quoted context omitted.

> The issue is fundamentally abstractions leak. Only people who've never used a good type system think this. Your function example demonstrates this pretty nicely: use a proper interval type for the two intervals and a proper return type rather than int, and then the answers to all your questions become obvious.

So say you replace it with a DateRange class so you end up with range1 = new DateRange(start1, end1); range2 = new DateRange(start2, end2); DateRange overlapRange = GetOverlap(range1, range2); You still don't know what happens when start1 is greater than end1? What happens when they have no overlap? What does overlap return if it doesn't return null? What happens if start1 and end1 are in local vs UTC time? What happ…

> You still don't know what happens when start1 is greater than end1?

That's DateRange's responsibility, it's not a concern for getOverlap. By the time you get as far as getOverlap you already have a valid DateRange. Small, reusable, compositional pieces.

> What happens when they have no overlap? What does overlap return if it doesn't return null?

It can't "return null", we're talking about using a decent type system here. The types should express what kind of failures are possible, so perhaps getOverlap returns a maybe, or an either where the left hand side is some structured "reason" value that expresses what went wrong.

> What happens if start1 and end1 are in local vs UTC time?

You get a compilation error because that distinction is part of the type.

> What happens if range1 and range 2 are in local vs UTC time?

You get a compilation error because that distinction is part of the type.

> When you get overlapRange.Duration. Days is this days in 24 hr increments or days by date?

That's Duration's responsibility, not getOverlap's; again, small, reusable, compositional pieces. You shouldn't ever be calling overlapRange.duration.days (obvious law of demeter violation); rather you should be calling something that can do the right thing with overlapRange.duration (e.g. display it, compare whether it's longer than some limit).

But to answer the question: check its type, that will tell you. Any value that you actually pass around and use in business logic should have a type that tells you what kind of thing it is; primitives should only ever be used within the very lowest level functions.

Re: Avoid Indirection in Code

#174
I wonder if the real solution to the indirection is better tooling.

If I can't easily see the contents of is_foolike, should I stop writing my code like this or should the editor be showing me these details more easily?

The real problem: it takes me too much time to see what's inside the function.

Re: Avoid Indirection in Code

#175
post #95
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):…

In many cases speaking of the foolike nature of a value is not dissimilar to speaking to the primeness of an integral: Is a value prime? And now here's is an algorithm for determining whether this value is prime. The motivation for many splitting out a prime-testing function is that primality testing is hard to do efficiently, not that there are many kinds of primes or many kinds of integrals. Indeed, in some languag…

Nothing is being abstracted here, and nothing is gained by indirection

Making the rest of the function readable would be a win. If the two functions in the if-then are quite long, and if that clause appears in many places, then that could be a big win in the aggregate.

Re: Avoid Indirection in Code

#176
post #2

That's a pet peeve of mine; I see it all the time when I work with lesser experienced developers, only I didn't know how to call it. I call it onion skin development, where the developer keeps hiding stuff in more layers of the onion, making my eyes water as I have to dig deeper and deeper to essentially find `a.foo(b)` under 12 layers of abstraction. They're so focussed on making everything look so purrty, they forg…

Indirection is a tool, and there are both good reasons and bad reasons to use it (as with most tools). A good reason would be to clarify the why or the what by "glossing over" the how : # Unclear: list_range = range(0, len(movies)) for i in list_range: j = randint(list_range[0], list_range[-1]) movies[i], movies[j] = movies[j], movies[i] # Clear: movies = shuffle(movies) Yes, you're "hiding" the actual steps (the "ho…

Honest question: isn't that kind of separating classes out into interfaces necessary for proper testing? When should you avoid splitting them out to interfaces and implementation classes?

Re: Avoid Indirection in Code

#177

I think some commenters don't understand the challenges of reviewing code contributions as an open source maintainer. Many people already feel like they're doing you a favour by contributing any code. This is amplified if they've followed "best practices" and broken it down into many tiny functions and objects. As a maintainer, having to review and maintain code that is abstracted in the wrong way is a major headache…

For my reference, can you give me an example of properly abstracted code and overly abstracted code?

Re: Avoid Indirection in Code

#178
post #104

Earlier quoted context omitted.

Agreed, although I think the example isn't too bad. I often see such "helper methods" - usually in a class called "utils" or "misc" - when (novice) programmers "try to plan ahead": how should I write this so it can be re-used? But KISS is remains a good principle. Predicting the future is hard. If in doubt, don't put in the extra indirection/abstraction. If/when the next person needs that functionality, they can refa…

In my mind I contrast "keep it simple" with "make it simple". Successful "make it simple" is better than "keep it simple", which in turn is much better than failed "make it simple". Every attempt at "make it simple" is a wager. Regarding the one-caller method, beloved by followers of maximum method length ideology, scorned by enemies of excessive indirection, I think we lost a very useful tool when the "structured pr…

I really like this pattern, but unfortunately some people consider it confusing when the closure capture is not explicit. C++ lambdas are nice in that regard.

Re: Avoid Indirection in Code

#179
"Avoid Indirection in Code" is a foolish thing to advocate, and the example provided is more confounding than explanatory.

Abstraction is the key. Abstraction can be used to reduce incidental complexity by hiding an implementation. Abstraction can be used to reduce the cognitive load of the essential complexity by making the code look like the problem domain.

But there is a cost to abstraction. A person reading your code may not have the same mental model of the problem, and might’ve be working at a higher or lower level of abstraction, in which case the "indirection" might be a stumbling block. Weigh these costs carefully.

Re: Avoid Indirection in Code

#180
post #160
post #121

Earlier quoted context omitted.

Disagree. Someone attempting to do something while unauthorized is oftentimes not an exceptional state and regular business logic depending on how access is given to different endpoints. Do not use exceptions, which are "heavy" (capturing call-stack etcetera), for something that commonly occurs.

Using an exception for authorization failures avoids TOCTOU security holes. Also, in many cases, there are intermediate layers of the call stack that don't have any useful contribution to failure handling; the best they can do is forward the failure return value up the stack. And if one part of an operation fails because of a lack of permissions, it is usually dangerous to continue processing without handling the fai…

This is interesting and compelling. I wonder about spambots though, when authorizing a service exposed to the web. Doesn't it get expensive pretty quickly when a bot decides to scan the site and floods it with requests that all exception?
Post reply on HN