Avoid Indirection in Code
181–190 of 220 posts
Re: Avoid Indirection in Code
#182Earlier 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.
Put another way: exceptions are exceptions to the normal program flow. They should not be used to communicate expected errors. I think of them as analogous to the `if (do() != 0) { goto out; }` paradigm of C.
Re: Avoid Indirection in Code
#183By substituting a series of lower level concepts/tasks/procedures into a single keyword the reader can more easily think about the problem domain. That's a good thing. If they know the abstraction, it reduces the number of things the reader must keep in their head.
However, if the reader is not familiar with the abstraction they will need to spend time to familiarize themselves. This can break down when trying to understand an abstraction leads to the "endless rabbit hole" problem. Basically, you have to open another file to see how that abstraction works, then it uses some other abstraction which you need to open another file to read, and so on. If the depth is too great the reader will lose context and won't be able to fit the problem in their head.
To avoid this, pull more of the functionality into the function instead of outsourcing it to an abstraction. Favor writing code that reads sequentially rather than as a series of jumps through a bunch of files.
As always, this is largely a matter of taste so use your best judgement and balance abstraction with pragmatism.
Re: Avoid Indirection in Code
#184Err, 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]] = {}
I've had issues reading code where I'm trying to trace control flow and I hit some dispatch point in the code and it's suddenly really hard to tell where to go from there. I do (did) a lot of Python programming and sometimes it's a little too dynamic, ya know what I mean? :-)
Re: Avoid Indirection in Code
#185Earlier quoted context omitted.
> "starts_with_foo" is a horrible name, because it only says how it is implemented, but doesn't convey any meaning or the intention of the function. Two questions for you: 1. If `starts_with_foo` only communicates how it's implemented, what about `starts_with_foo(x)` communicates to you that it's implemented as `x.startswith('foo')` rather than `x[:3] == 'foo'`, or some other implementation? 2. If the intention of th…
Yes, it only says that we are checking that the first three characters are 'foo'. Exactly which commands you are using is irrelevant. You are telling us what we are doing on a too low level. With your logic anything that doesn't spell out exactly which assembly language commands that are used won't be implementation details What is a bug? If the name of the function is IsFoolike, the definition of the function will b…
Nothing in `starts_with_foo` describes what commands are being used--it describes what it does, not how it does it. With your logic, telling what the function does at all is too low a level.
> If the name of the function is IsFoolike, the definition of the function will be the authorative place in the code that tells us what "isFoolike" means.
Okay, so why not just name your functions, `aaaaa`, `aaaab`, `aaaac`, etc.? If the name of the function is `cdyfj`, then the definition of the function will be the authoritative place in the code that tells us what `cdyfj` means.
Aren't you worried that `isFooLike` gives us too much information about the implementation? After all, the string "foo" is in the implementation. What if you want to test for "bar" later? Then you'll have to change all the places where `isFooLike` is used, in addition to the definition!
> What is a bug?
1. You're claiming that the name should tell us what the intention of the function is.
2. You're claiming that the intention of the function isn't `starts_with_foo`.
3. The function tests to see that the string starts with 'foo'.
The only way all three things can be true is that the intention is if the function does something it's not intended to do. That's the definition of a bug.
> What do we mean when we say that something is Foolike?
The fact that nobody could possibly answer this question is exactly the problem I'm pointing out. You might as well just name the function `cdyfj`, because `isFoolike` doesn't actually give you much more information than `cdyfj` does.
Re: Avoid Indirection in Code
#186Earlier quoted context omitted.
well technically, if you type-annotate the dict you should be fine: dispatch: Mapping[str, Callable[arg_types, ret_type]] = {}
That's neat. I hadn't realized type hints had gotten that good now. Cheers! I've had issues reading code where I'm trying to trace control flow and I hit some dispatch point in the code and it's suddenly really hard to tell where to go from there. I do (did) a lot of Python programming and sometimes it's a little too dynamic, ya know what I mean? :-)
personally i like these dispatches more than giant if/switch statements for no reason other than better separation and management of logic. as for debugging - stepping through code in ipdb is still the way to go.
Re: Avoid Indirection in Code
#187Re: Avoid Indirection in Code
#188It'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…
Yes. Also that there are many ways to do it, the details of which you don't necessarily care about.
> Nothing is being abstracted here
It absolutely is. To abstract is to ignore differences. Here you are abstracting over the methods used to do primality testing. If you are using "1&p:" in J to test if something is prime, you are saying something like: "I don't care exactly how it's done, just that it uses one of the reasonably fast and correct algorithms available."
> nothing is gained by indirection: Does this is-prime routine even deserve a name?
I am so baffled by these claims that I wonder if I'm missing your point.
My reaction is: A tremendous amount is gained by this abstraction. It is exactly the abstraction you want most of the time. And yes, it deserves a name, because "Is it prime?" is how everyone thinks about it and talks about. It links up with the whole history and culture of mathematics.
Re: Avoid Indirection in Code
#189Earlier quoted context omitted.
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.
The nice thing about abstraction is that we don't need to go look up the definitions (unless we're tracking down a bug that's in those parts!): we can read a line like `if checkHasPermission(x):` and get the gist of what's happening, and skip over it if we don't care about the permission system at the moment.
Re: Avoid Indirection in Code
#190Earlier quoted context omitted.
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?
In terms of when to split them out, my rule of thumb is "when there's more than one implementation class that does the same function".
Good examples are things like Comparable/Comparator, Reader, Writer, Serializable -- these are all descriptions of behavior for which multiple implementations exist. Interfaces, in general, are adjectives (or at least adjective-ish).
Bad examples are things like CheckoutService, ShoppingCartManager, JwtTokenManager -- these are all descriptions of specific components within an application that have a single implementation. These are nouns, and generally they're a sort of "proper noun" (in that they refer to one single thing by its name). If you don't already have many implementations for the behavior they implement, then you don't need an interface (since an interface is just a way of describing a set of "external-facing" contracts which many specific implementations may fulfill -- like List vs LinkedList/ArrayList).
The reason why is simply that it's just waste. It's more code for more code's sake. At best, you're just clicking "go to definition" one extra time, and at worst your code is confusing (because someone might assume the presence of the interface means it's "swappable" behavior, rather than integral application logic).
The "YAGNI" ("You Aren't Going To Need It") principle applies here: if you don't know you're going to need it, don't build it. Once you do know that you're going to need it, then you build it. Anything before that point winds up being wasted time/effort.