Live data from Hacker News

Avoid Else, Return Early (2013)

blog.timoxley.com

551–560 of 601 posts

Re: Avoid Else, Return Early (2013)

#551
post #197

Earlier quoted context omitted.

For whatever it's worth, I insisted on single return for a long time when I was an intermediate programmer. 30 years in, I strongly prefer return early (as well as continue early in loops) and I cannot go back. The immensely reduced indentation is marvelous. Having that 'happy path' consolidated is more readable.

I find it interesting how most of the comments about return early are based on experience. I had the same thing happen about 10 years in. I just got sick of the extra coding and long indented blocks. And just switched over one day. Why is this not taught from day one?

We were taught "one entry, one exit".

I now think of the early part of a function as a filter for bad params, invalid state & whatnot, and return as early as possible.

Code just feels less complex that way.

Re: Avoid Else, Return Early (2013)

#552
post #314

Earlier quoted context omitted.

I always tell my team that deleted code is the best code. Obviously less code is often more maintainable but there is also the element of being willing to throw away stuff you did earlier and not being attached to it.

I have a similar take on this, but expand it a bit by arguing that any line of code added – no matter how innocuous – is a liability. It's a line of code that has to be maintained, with all of the responsibilities that come with that. It may be a line that's responsible for adding more value than it costs, but it's still a liability. It won't add value forever, most likely, and when it stops it'll just be debt. It ma…

Yes! Every addition carries some risk. The real world example I give to clients/managers of the risk of adding a feature is this:

Once I was using an industrial camera that had a mode that allowed it to mirror or “flip” the image output. Unfortunately, after running for a few hours, sometimes it would decide to flip the image on its own. Had that “flip” feature not existed, the bug could never have occurred.

The point isn’t that you shouldn’t add useful features, but that even easy to add features aren’t as “free” as they might seem and the risk of adding them should be recognized and weighed against their utility.

Re: Avoid Else, Return Early (2013)

#553

Earlier quoted context omitted.

Refactoring might have you adding a parameter to a function, and then feeding that additional parameter to the function at it's call sites. Even when you only have one call site and thus are in the "2 functions" case - these two functions are often separated by multiple "irrelevant" functions. The less scrolling, tab management, and other context switching I have to do to look at those "2" functions, the easier it is…

> The less scrolling, tab management, and other context switching I have to do to look at those "2" functions, the easier it is on my memory. I don't disagree, but the sentiment seems to be "fit everything on the screen at all times", which seems like one of those rules that may take more time to implement than it saves in actual time. I work on projects with upwards of 100k lines of code consistently, the very idea…

I tend to work on gamedev codebases roughly near a 1mloc (both above and bellow.) In one recent refactoring I recently managed to get a class declaration (that was previously local to a single .cpp and had all functions defined inline in one go) to fit on a screen. I use small fonts, 4k resolution monitors, portrait orientation, and I still had to cull some "GetFoo gets a foo" style worthless doc comments to accomplish it. (I kept the comments that actually added anything of meaning.)

Given that above description, you can probably guess I don't have everything on screen at all times, not even close. But I'll refactor off small files of related functionality where I can, subdivide things that are getting unwieldy, etc. - put private/static functions near the public/exported functions that call them, break things up into (sub)modules, etc. and get good grouping.

> I don't disagree, but the sentiment seems to be "fit everything on the screen at all times", which seems like one of those rules that may take more time to implement than it saves in actual time.

Taken to the extreme, any such rule absolutely will. I certainly don't have that as a hard and fast rule. Or even as a guideline per se - it's more a faint but distant dream, or perhaps a feverish hallucination, a reminder of codebases past. But there's often plenty of cruft and low hanging fruit that can be fixed up in these codebases. Everyone has their "just one small change" to check in, so things build up and they don't jump to "okay, this is one line too many - time to refactor."

I practice pain driven development - when it's getting to be a pain to keep enough of something my head to do whatever sweeping changes or overhauls I might need to do, see if some pure or near-pure refactoring changes it. No expected behavior changes, often not even class or structural changes - often literally just regrouping code by topic and category via cut and paste. Horrific diffs, but low-risk checkins, and often a lot easier to reason about afterwards, and suddenly related code is often on the same screen.

(Although far from always.)

Re: Avoid Else, Return Early (2013)

#554

Earlier quoted context omitted.

1) I frequently tell my diffs to ignore whitespace so I can see my structural changes without getting drowned in a sea of indentation changes when introducing new scopes. Not viable when diffing python. 2) I semi-frequently make indentation mistakes when resolving merge conflicts. In braced languages this is fixed by an autoreformat. In unbraced languages, I have to take a lot more care with merging, lest I end up in…

In this specific case if (a) { print(a) } and if (a) { print(a) } Could just be if (a) print (a) Except in perl, where it's print (a) if (a) And if you want to add a second line you have to change it, thus I got into the habit of if (a) { print(a) } But I don't code for a living, any more than I run network cables for a living, or screw things into bays for a living. I code as a tool to get the job done, most recentl…

I do like one-liner conditionals (at least short ones for early bailout) but sadly my coworkers and their coding standards do not.

Re: Avoid Else, Return Early (2013)

#555
post #311
post #266

Earlier quoted context omitted.

That's covered by "all things equal". The argument against placing braces on their own line is that this: if (a) { print(a) } conveys exactly as much information as this: if (a) { print(a) } while taking up more space. The thing the brace tells you is already told by the indentation, so the brace is on a superfluous line.

> The thing the brace tells you is already told by the indentation Which is true, and of course begs the question: why do you even need the braces?

Off-topic and pedantic pet peeve, would you mind replacing "begs the question" with "raises the question". The former might not mean what you intended.

Re: Avoid Else, Return Early (2013)

#556
post #142

Earlier quoted context omitted.

This is like the 10 commandments. Good ideas mixed with stuff that is a matter of taste :) If it was this simple we wouldn't be still arguing about coding styles decades after they were invented. I know few old programmers that I respect very much, and they don't agree about the perfect coding style, not even simple stuff, like braces in separate lines or not.

> If it was this simple we wouldn't be still arguing about coding styles decades after they were invented. I disagree with the logic in this sentence. New coding styles are invented all the time. The guard statements in the article were only formalized in the late 90s, for example. These arguments about coding styles "decades after they were invented" are the only way we know which work and which don't. It's not a ma…

Late 90s were 2 decades ago :)

Re: Avoid Else, Return Early (2013)

#557
post #465

Programmers with lots of hours of maintaining code eventually evolve to return early, sorting exit conditions at top and meat of the methods at the bottom. Same way you evolve out of one liners. Same way comments are extra weight that should only be in public or algorithm/need to know areas. Same way braces go on the end of the method/class name to reduce LOC. Same way you move on from heavy OO to dicts/lists. Same w…

> Same way you move on from heavy OO to dicts/lists. Is that a thing? No question that many concepts of OOP are in heavy need of reform, but I didn't know the basic idea of a struct was one of them. I can sort of imagine this for quick-and-dirty things in untyped languages - but if you have types, passing dicts around everywhere seems like needlessly throwing away type safety - while it's also cumbersome to code and…

Using dicts/lists is more apt to flexible serialization/deserialization, versioning, changing data structures in fields/structure and are great for data/api prototypes that move into MVPs and eventually alpha/beta if not the final form.

OO can run into versioning issues with fields/structures changing and needing to have many versions of those over app iterations just dicts/lists. Database fields being added and removed and supporting old versions is more easily solved in basic dicts/lists that map to json/xml and the base of any language. Dicts/lists are very interchangeable across systems and are the base of API outputs and the ease of use in javascript, python and other dynamic uses.

There should be a reason you are using structured OO but many times it becomes as weight, though there are good times to use it and most projects have some of it. Good reasons are hardened/non-changing codebases, possibly native apis/libs to help understanding, but when it comes to rest/http apis usually you are building OO that consumes data simply to return to dicts/lists when output/input into the apis/client-side/etc.

Classes/OO/types can be built as well with objects that are based on dicts/lists that do have some internals and helper methods that get/set keys and values and perform actions if you don't want to do that with another context class/api, or wrap dict/lists so that the serialization/deserialization and versioning issues are not a problem as the flexibility of dicts/lists and the benefits that brings in simplicity are still there but there is more structure where needed.

Re: Avoid Else, Return Early (2013)

#558

Earlier quoted context omitted.

The functional style that you show certainly has multiple points where a value is returned, so you could call it "early return"; even if as a syntactic shorthand the keyword "return" is omitted. This is common across a lot of functional languages.

> The functional style that you show certainly has multiple points where a value is returned, so you could call it "early return"; It's not "early return" any more than: function(err, results) { if (!err) { // handle results } else { return handleError(err) } }

Right, though I was thinking of other examples of functional match style and returning a value. As seen in in Haskell, F# Rust etc.

e.g. https://doc.rust-lang.org/book/second-edition/ch06-02-match....

So value_in_cents can return a value of 1, 5, 10 or 25.

Saying that it "does not have a return statement" might be truish - but the fact that the "return" keyword is not needed to return values is just syntax.

Saying that there's no early return / multiple exit points is not so much. To my eye there are 4 places where that function can return a value. And the cases might not be simple numbers making the whole thing an expression, later examples on that page show how the cases can differ in side-effects.

Re: Avoid Else, Return Early (2013)

#559

Earlier quoted context omitted.

I always tell my team that deleted code is the best code. Obviously less code is often more maintainable but there is also the element of being willing to throw away stuff you did earlier and not being attached to it.

I remember teaching a guy about YAGNI - You Ain't Gonna Need It. He would write helper functions that he thought would be useful before writing actual code and often ended up wasting time. Half the functions he wrote, he never actually used, but he'd still spend time writing them and unit tests for them.

Its interesting that University puts an emphasis on re usability, when in reality it often makes code a lot more difficult to understand.

Re: Avoid Else, Return Early (2013)

#560

Earlier quoted context omitted.

Oh but it is, independent of the language. Fewer LOC means more code on the screen, which means you can more easily grasp the functionality of some piece of code, which makes it easier to maintain.

> Fewer LOC means more code on the screen, which means you can more easily grasp the functionality of some piece of code, uh ? for me the less code on each line and the easier it is to read

That depends. Python's pep guidelines has an 80 char wide rule, but I personally find that a lot harder to read than one long line (most of the time).
Post reply on HN