Live data from Hacker News

Avoid Indirection in Code

matthewrocklin.com

191–200 of 220 posts

Re: Avoid Indirection in Code

#191
post #164

Earlier quoted context omitted.

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…

> 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 Nothing in `starts_with_foo` describes what commands are being used--it describes what it do…

> With your logic, telling what we are doing at all seems to be too low a level.

No, we need to say what we are doing on the right semantic level for that part of the program. It is rarely describing exactly what we are doing, because then only function of functions will be to abbreviate common patterns of code. The main function of functions is to abstract. At this particular level, Foolike has a semantic meaning to the reader of the program who knows what a Foo is and what isn't. And in this particular version of the program we happened to implement it from the first characters of the string. But maybe later we decide that it anything that inherits from a Foo, or has the Foo interface, or just walks, but not necessarily quacks, like a Foo.

And the reason I don't call the function some nonsense is because then it is hard for the readers of the code to understand the meaning of it. And it might not help even if the they read the definition of the function, because the definition isn't the meaning. It doesn't say anything about why it is interesting to know if the string starts with 'Foo' or not (which is, as I have mentioned, the problem with the name you suggested).

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

No, we already know what foolike means. It is part of the domain that we are working with. The people who wrote the code and the people who read the code are assumed to have enough domain knowledge to know what a Foo is, and they understand me when I say things "we better use a Foolike here, otherwise we'll get a Bar problem".

Let's use a bit less abstract example. Say I implement the function NextInvoiceNumber(). My first implementation might be something like {return ++n} (because I'm making a proof of concept and I'm in a hurry). You seem to argue that a better name of the function would be something like AddOneToNAndReturnN(). But then the reader of the code won't understand what the purpose of the function is, because nothing says anything what n is good for and why we are interested in getting the next n. And the moment I start to add different number series for different articles and some of them increment with 10 and some with 100, the name AddOneToNAndReturnN() will be wrong. That will not be a problem for NextInvoiceNumber(), because that is perfectly normal for anyone who has done any work with invoicing. Invoicenumbers are part of the domain that the developer must know about.

Re: Avoid Indirection in Code

#192

Indirection [0] is a quite big topic and applying it can result in both significantly improving the code and getting more problems than benefits. Therefore, the question is when indirection is appropriate. One opinion [1] is that "We can solve any problem by introducing an extra level of indirection" Another opinion is expressed in this post (avoid indirection). In the context of this example, indirection is related…

I always liked the second part of this quote: "…except for the problem of too many levels of indirection"

Re: Avoid Indirection in Code

#193
post #191

Earlier quoted context omitted.

> 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 Nothing in `starts_with_foo` describes what commands are being used--it describes what it do…

> With your logic, telling what we are doing at all seems to be too low a level. No, we need to say what we are doing on the right semantic level for that part of the program. It is rarely describing exactly what we are doing, because then only function of functions will be to abbreviate common patterns of code. The main function of functions is to abstract. At this particular level, Foolike has a semantic meaning to…

> At this particular level, Foolike has a semantic meaning to the reader of the program who knows what a Foo is and what isn't.

It sure doesn't. Maybe readers are much smarter than me, but I don't know that it means to be foolike until I read the function definition. Apparently something is foolike if it is a string starts with 'foo', but I certainly wouldn't be able to guess that from the name. And I still don't know what a Foo is or isn't.

> But maybe later we decide that it anything that inherits from a Foo, or has the Foo interface, or just walks, but not necessarily quacks, like a Foo.

Okay, so you're naming this based on what it might do in the future rather than what it does? How are you predicting the future here? And when it does the future thing, will you change the name so that it is talking about the future again, since you have such an objection to names referring to what a function presently does?

> And the reason I don't call the function some nonsense is because then it is hard for the readers of the code to understand the meaning of it.

The concept of being like a food IS some random nonsense: it's hard for readers of the code to understand the meaning of it.

> Let's use a bit less abstract example. Say I implement the function NextInvoiceNumber(). My first implementation might be something like {return ++n} (because I'm making a proof of concept and I'm in a hurry). You seem to argue that a better name of the function would be something like AddOneToNAndReturnN(). But then the reader of the code won't understand what the purpose of the function is, because nothing says anything what n is good for and why we are interested in getting the next n. And the moment I start to add different number series for different articles and some of them increment with 10 and some with 100, the name AddOneToNAndReturnN() will be wrong. That will not be a problem for NextInvoiceNumber(), because that is perfectly normal for anyone who has done any work with invoicing. Invoicenumbers are part of the domain that the developer must know about.

Okay, I'm guessing you mean something more like:

    int getNextInvoiceNumber() {
        return ++this.invoiceNumberCounter;
    }
And that's perfectly reasonable, because getNextInvoiceNumber describes what the function does.

A name like addOneToInvoiceNumberAndReturnInvoiceNumber would be bad, not because it's inaccurate, but because it doesn't tell you what the function does in context.

We don't have any similar context to concern ourselves with for 'starts_with_foo'.

It seems like going back to the beginning of the discussion, you guys assigned some meaning in your heads to the adjective "foolike" that you didn't choose to share with me and decided based on this meaning in your head that I was wrong. Perhaps the name `is_foolike` is a better name within the imaginary context you've created, but I was talking about the code that was linked on this thread, not the code you've imagined and didn't tell me about.

Re: Avoid Indirection in Code

#195
post #188
post #95

Earlier 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…

> The motivation for many splitting out a prime-testing function is that primality testing is hard to do efficiently 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…

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

    addOne =. 1+[
    isPrime =. 1&p:
I do not think these functions are useful enough abstractions to be given a separate functions.

Re: Avoid Indirection in Code

#196
post #191

Earlier quoted context omitted.

> With your logic, telling what we are doing at all seems to be too low a level. No, we need to say what we are doing on the right semantic level for that part of the program. It is rarely describing exactly what we are doing, because then only function of functions will be to abbreviate common patterns of code. The main function of functions is to abstract. At this particular level, Foolike has a semantic meaning to…

> At this particular level, Foolike has a semantic meaning to the reader of the program who knows what a Foo is and what isn't. It sure doesn't. Maybe readers are much smarter than me, but I don't know that it means to be foolike until I read the function definition. Apparently something is foolike if it is a string starts with 'foo', but I certainly wouldn't be able to guess that from the name. And I still don't kno…

We where using what was obviously an abstract example. We could imagine that Foolike was some property that was meaningful for some domain that someone worked in. You do seem to have problems with abstractions in general. And in the end, when I realised what you needed to understand, even you understood.

Re: Avoid Indirection in Code

#197
post #195
post #188

Earlier quoted context omitted.

> The motivation for many splitting out a prime-testing function is that primality testing is hard to do efficiently 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…

> 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. addOne =. 1+[ isPrime =. 1&p: I do not think these functions are useful enough abstractions to be given a separate functions.

> addOne =. 1+[

The J authors did (correctly imo) think that it was. It's called ">:".

> isPrime =. 1&p:

In this case it does not, although I'd argue it's not so much about "isPrime" not being a useful concept worth naming on its own, as that J is a dense language working with a limited character set and there are a bunch of related concepts around "primeness" that need to packed into the "p:" verb -- accomplished in this case allowing the left argument to access a bunch of different verbs worth naming. Said another way, "1&p:" just happens to be the way J names this concept, for reasons of economy.

For the prime case in particular, it will be a named function in most math libraries that deal with such things, or languages where such math is central to the domain:

https://reference.wolfram.com/language/ref/PrimeQ.html

I am still unclear what your claim is: that such functions are always an example of "too much abstraction"? If so, I don't think that claim is tenable.

Re: Avoid Indirection in Code

#198
post #100

Ok, to take a real world example instead: if (url.startsWith('http://')) { vs. if (isAbsoluteUrl(url)) { If the next developer comes by in 2 months to fix the case for https:// urls (and protocol relative ones in 6 months), they'll immediately be able to spot the intention of the code and can easily fix it in the abstraction layer that's already in place. Moreover, the fix will be applied every other place this fault…

url.startsWith('http://') may be better, especially if used only once.

For example, what is "isAbsoluteUrl()" supposed to do? Does it match anything that starts with "word:", maybe "word://" maybe it is limited to supported protocols, what about validity, encoding, etc...

For example "mailto:foo@bar.com" is an absolute URL, and a correct one that will work in your browser, but if your intention is to download something, it will fail. In fact, just by looking at isAbsoluteUrl(url), I don't have any idea about when the condition will be true.

Maybe you want something more explicit like isAbsoluteDownloadableUrl(url). But still, it doesn't tell you everything, in particular, it doesn't tell you what kind of checking it does. Is it a full validation or just a quick check? And sure, you can add documentation and all that but in the end, that's typically how you add in 50 lines of code for no good reason.

url.startsWith('http://') may not be generic but what it does is crystal clear. And maybe, when https support will be added there will be a bug. But if isAbsoluteUrl() is just "return url.startsWith('http://')", it is still be a bug.

I am not saying that isAbsoluteUrl(url) is bad, just that the article also applies to your "real world" example. And as mentioned in the end of the article "functions are still a good idea ... there is some balance to find here".

Re: Avoid Indirection in Code

#199

Earlier quoted context omitted.

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?

I'm curious what you mean by "necessary for proper testing". I know a lot of people use that pattern (single-implementation interfaces) for use with a dependency-injection container, and then they mock the interfaces ...but that doesn't buy you any more than mocking the implementation classes themselves would. In terms of when to split them out, my rule of thumb is "when there's more than one implementation class tha…

Oh, so basically don't use interfaces unless you actually need the functionality provided by interfaces (namely, polymorphism). Got it!

Re: Avoid Indirection in Code

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

agree, like you said it's probably a teaching thing. in e.g. python, javascript, and others, nested functions are available and first-class citizens. (okay, those languages have other issues)

really, there isn't a substitute for experience, but we can try. and again, the devs who do this mean well, they just don't know better yet. that's still better than apathy

Post reply on HN