Live data from Hacker News

Avoid Indirection in Code

matthewrocklin.com

201–210 of 220 posts

Re: Avoid Indirection in Code

#201

Earlier quoted context omitted.

Ok, I’m curious. Let’s say that I want to fall into that if statement if my product is named something like “foo”, costs under $100, isn’t discontinued, and has sold over 100 units in the last month. Fine, you can say now I have overcomplicated the domain and I should go back to requirements, but these complicated things do happen. Surely it’s acceptable to name my function “isFooLike” rather than “startsWithFooAndCo…

> Ok, I’m curious. Let’s say that I want to fall into that if statement if my product is named something like “foo”, costs under $100, isn’t discontinued, and has sold over 100 units in the last month. Fine, you can say now I have overcomplicated the domain and I should go back to requirements, but these complicated things do happen. Surely it’s acceptable to name my function “isFooLike” rather than “startsWithFooAnd…

This is a really, really terrible way to name functions, or anything. I have seen code that tried to put the spec in the name, and it was awful.

A name that is long enough to tell you all you might need to know about what the thing named does is too long to be usable at all.

A name needs to be long enough to distinguish it from the other things being named. That's what naming means. If it also gives you a hint about which thing, of the things named, it really does, that makes it perfect.

Anything beyond that adds cognitive load, making it exponentially worse with each syllable added.

Re: Avoid Indirection in Code

#202
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):…

Great explanation, but I would just say `hasPermissions`. I like to name functions for their result, not for what they do.

Re: Avoid Indirection in Code

#203
post #128
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.

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.

> Put another way: exceptions are exceptions to the normal program flow.

There's no iron law or mathematical proof that says this is optimal. It's just a preference some people have. Exceptions are the error handling mechanism for many programming languages. Authorization errors are a type of error. Under this construction, exceptions are the appropriate way to handle authorization errors, including when a principal attempts to access an resource they are not authorized to access.

Re: Avoid Indirection in Code

#204
post #197
post #195

Earlier quoted context omitted.

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

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

So did CL. I still disagree though: >:/ is different than (1+])/. 1++:i. is the same number of characters as >:+:i. I feel like there are many more valuable functions more deserving than this one.

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

Sorry it's so difficult to figure out; You're not asking really constructive questions, so maybe one of my other replies will help. I'm not saying anything is always one way or another because I think the only absolute is that it always depends!

Re: Avoid Indirection in Code

#205
post #152

Earlier quoted context omitted.

When a Computer Scientist or a Mathematician says "abstraction" they do not mean the same thing that a layperson means. This is what I am referring to: https://en.wikipedia.org/wiki/Abstraction_(mathematics) Abstraction in mathematics is the process of extracting the underlying essence of a mathematical concept, removing any dependence on real world objects with which it might originally have been connected, and gene…

It's not about the name, it's about the boundaries of the concept , but how do humans establish a concept and it's boundaries without giving it some kind of name to refer to? But I am may not be familiar with the kinds of languages you speak of, where there are no variables (and no functions/procedures? No names at all?) but "still have very powerful expression-building capabilities." I'm not totally sure what you're…

> I am may not be familiar with the kinds of languages you speak of, where there are no variables (and no functions/procedures? No names at all?) but "still have very powerful expression-building capabilities." I'm not totally sure what you're talking about, i admit.

I venture you probably are familiar with them (even at least somewhat), but you might not think of them as languages. Or perhaps you are turned off because they are oblique.

Regular expressions are a good example, because they're really common. Here's a program I use frequently:

    "([^"\\]*(\\.[^"\\]*)*)"
Incidentally, this shows off an interesting and useful power of grouping. Too many people learn grouping and capturing at the same time, and capturing seems so much more useful that the second use of parenthesis is missed.

Another good example to consider is pointfree Haskell[1]. This is typically sold to programmers as a kind of golf, but this combinational style can make it possible to spot optimisations that are difficult in a pointful implementation. If you don't like types, you also see a lot of this in languages in the Forth family: Simple "improper" functions like : K 1024 * ; are really common, with no effort at all to give names to arguments of short functions, but people sure do like their stack diagrams...

[1]: https://wiki.haskell.org/Pointfree

> It's not about the name, it's about the boundaries of the concept, but how do humans establish a concept and it's boundaries without giving it some kind of name to refer to?

Consider the following function:

    def m(f,a): [f(x) for x in a]
It is very easy to come up with a name for "m" but it is difficult to come up with a useful name for "f" or "a", so we typically don't. In APL we call f ⍺⍺ or ⍵⍵, and a we call ⍺ or ⍵, and you typically do not name your arguments at all (you can, of course), but it's very liberating not having to think of names for these things! When using other languages, I will typically name function-arguments f or g, and non-functional arguments x and y, but this is just a convention I use.

Of course we would never need to write m in APL because it's just ¨

> I think taking logic and putting it in a procedure with a name is the fundamental tool of abstraction offered by such an environment.

Your brain is the fundamental tool of abstraction!

If you want to create a ledger for tracking deposits and withdrawals from an account, you may build an abstract transaction log that can contain any values - and you could just as easily be able to use it for a bank account as for a shopping cart or fuel tracking in an airplane. And yet, being "too abstract" may mean the code is too hard to use (successfully, or it's too slow, or whatever) - it might be so much easier to build something simpler and more concrete, even if it means you have to write "basically the same thing" when you decide you're done making bank accounts and it's time to start making shopping carts.

The thing to keep in mind is that it's you deciding when it's easier and when it's harder. That's why so much of programming seems subjective, and people have strangely confusing rules, because we don't all read code the same way; we don't all have the same software experiences that lead us to think a certain way.

Re: Avoid Indirection in Code

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

Seems to me that this is a great example of how, if I'm the next developer, I will have to go dereference isAbsoluteUrl in order to see that the https:// case hasn't already been handled--whereas it would have been obvious with the inlined version. Maybe isAbsoluteUrl is imported from a module I trust, and I won't think to go look at it for the problem. And "every other place" is a bit of a strawman, since everyone commenting seems to think if you have three cases of a thing, then the abstraction is warranted... so it'd have to be "in the other place".

Re: Avoid Indirection in Code

#207
If you have defined the function and it is used in many places then you only need to read it once and remember everytime you come across it. How is recoding it every time going to help the readability. I think you just need to make sure the functions purpose is clear either.

Re: Avoid Indirection in Code

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

Just to add to lmm's comment:

> What happens when they have no overlap?

You get the empty range. Because any sane modeling of a range will have an empty value for completeness, and modeling it without those overlaps and date specialization clobbering your screen will make it patently obvious wether your type is complete or not.

And if you still forgot to include the empty value, you won't be able to write the overlap function, so you will have to go back.

Re: Avoid Indirection in Code

#209
post #125

Earlier quoted context omitted.

The issue is fundamentally abstractions leak. And if the code is overly "Uncle Bobified" the abstractions will leak bugs. On the other hand if you under "Uncle Bobify" the code will be very difficult to read because you won't be able see the forest for the trees. This is one of the advantages of comments and local functions. You can inline a function an add a comment. With the comment providing the abstraction and th…

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

> use a proper interval type for the two intervals and a proper return type rather than int

You can do that with a "bad" type system like C's, but types themselves are a leaky abstraction with many usage decisions needing to be aware of underlying details of the type. Should the variable be stack allocated? Depends on the size of the underlying type and whether it's static. Can I add a variable to this type? Depends on it's usage, it might break binary compatibility.

In the case of type systems the abstraction comes with more than enough benefits to make it worthwhile, but it still leaks.

Re: Avoid Indirection in Code

#210

While the example in the article isn't great for making the point I actually agree with the main idea. I understand the arguments for 'Uncle Bobifying' code but I think, as the article says, there's a balance to be struck. It's highly likely the next time I see your code (or my code if I'm coming back to it a month or two later) is when I need to fix a problem with it. While it's useful to have it split up into logic…

The issue is fundamentally abstractions leak. And if the code is overly "Uncle Bobified" the abstractions will leak bugs. On the other hand if you under "Uncle Bobify" the code will be very difficult to read because you won't be able see the forest for the trees. This is one of the advantages of comments and local functions. You can inline a function an add a comment. With the comment providing the abstraction and th…

One of my considerations is you have two types of functions. Ones that are input complete and ones that aren't. The former produces a 'rationally correct' result for every input. And the latter doesn't. Writing the former is hard and proving even harder. Keeping potentially suspect functions local and limited in scope is arguably more conservative.
Post reply on HN