Live data from Hacker News

Avoid Else, Return Early (2013)

blog.timoxley.com

301–310 of 601 posts

Re: Avoid Else, Return Early (2013)

#301
post #6

A more formal name for this approach is / could be Guard Clauses: https://refactoring.com/catalog/replaceNestedConditionalWith... . This pattern has been elevated to a language construct in Swift: https://thatthinginswift.com/guard-statement-swift/

Yes, I love guard clauses!

I work in Python, and one thing I do if I end up needing to use the same guard clause in several functions is to turn it into a decorator. That way, if I need to update the logic, I can do it in one place and have it propagate everywhere and it makes the function's dependencies clear at first glance.

This was my introduction to aspect-oriented programming, which I've thoroughly fallen in love with.

Re: Avoid Else, Return Early (2013)

#302

Earlier quoted context omitted.

>braces go on the end of the method/class name to reduce LOC You could argue the placement of braces with lots of valid arguments both way, but this... to reduce LOC ? Doesn't feel like a valid reason in any language using braces...

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

Re: Avoid Else, Return Early (2013)

#303
post #6

A more formal name for this approach is / could be Guard Clauses: https://refactoring.com/catalog/replaceNestedConditionalWith... . This pattern has been elevated to a language construct in Swift: https://thatthinginswift.com/guard-statement-swift/

I was more or less required to use guard clauses by my manager at my previous job. I used to hate being forced to use them, just because as a recent computer science graduate I was happy with nested conditionals everywhere. But guard clauses make life so much easier, I eventually discovered. They make testing your code a lot easier, and also make reasoning about the code and reading it so much easier. Ruby makes writ…

guard also lets you unwrap your optionals and have them in scope for the rest of the function.

Re: Avoid Else, Return Early (2013)

#304
post #271
post #257

Earlier quoted context omitted.

I see it the same way. It offers symmetry. The method name can be as long or short. At least the start of the code block and the end is easily visible since the indentation level remains same. Gives a sense of Python readability.

Huh? My emacs auto-indents both styles in exactly the same way: void foo() { code...; } void foo() { code...; }

Great for you. I guess you never have to share your code with other people; especially those who aren't using emacs.

Re: Avoid Else, Return Early (2013)

#305
This blogpost's analysis is incomplete. It needs to discuss the core issue with WHY C programmers originally avoided multiple return statements. Its pretty simple:

    void function(){
        lock_mutex();
        void* thingy = malloc();

        if(...)
        {
            return; // Bug!! You forgot to unlock the mutex
            // Bug!! You forgot to free(thingy);
        }

        // Imagine 100 lines of code here
        
        free(thingy); // Hard to remember with 100 lines of code in the way
        unlock_mutex();
    }
When functions grow to hundreds of lines of code, you need to ensure that all cleanup functions are called. Modern programming languages have features to handle this case "automatically" (Python "with", C++ RAII, Java finally). Which is why its fine today.

Re: Avoid Else, Return Early (2013)

#306
post #247

Earlier quoted context omitted.

You could use a std::unique_ptr with a custom deleter: http://en.cppreference.com/w/cpp/memory/unique_ptr

This is a reasonable suggestion since the code isn't too bad. std::unique_ptr > p(new Foo, [](Foo* p) { if (p) destroyFoo(p); }); // initialize Foo and set to NULL if failed It increases complexity a bit because you no longer have a simple pointer, and you can't allocate on the stack anymore (my example should have declared `Foo foo;` with `destroyFoo(&foo)`, sorry for typo.)

If you didn't want a pointer you could have just used something like:

    template
    struct FooWrapper {
        TFoo foo;
        const TDestr destroyFoo;

        template
        FooWrapper(Tinit initializeFoo, TDestr destroyFoo) : destroyFoo(destroyFoo)
        {
            foo = initializeFoo();
            if (!foo)
                throw FooException(...);
        }

        ~FooWrapper() { destroyFoo(&foo); }
    }
Although really that should have been part of the "Foo" class itself, but if you need to deal with external code I suppose that might not be possible.

Re: Avoid Else, Return Early (2013)

#307
post #158

Earlier quoted context omitted.

> The problem with this is maintaining comments. That's like saying "the problem with healthcare is that it costs money". Of course comments can get out of date. The solution isn't to throw them out!

There's no alternative for healthcare. There's alternative to in-code comments answering the question "why" - it's commit messages. They can't be out of date.

But commit messages can easily get plowed under in some annoying "reformatted, I hate tabs/spaces" commit. I do like to see some "why" sprinkled in here and there that will survive those accidents. But I will nonetheless prefer blame output if it is still meaningful. Also, I enjoy a commit that removes redundant comments almost as much as one that removes redundant code.

Re: Avoid Else, Return Early (2013)

#308
post #58

Earlier quoted context omitted.

You could still write it as fun max(a, b) { if a > b { return a } return b } Not saying this is necessarily better or worse. The point I want to make is that your case isn’t special.

I am arguing this is worse, since it expresses the logic in a more convoluted way.

>I am arguing this is worse, since it expresses the logic in a more convoluted way.

Ok, I really wonder if in this special case the logic just looks _odd_ because of bracing styles. (bear with me)

This is very easy to understand, where as the parent example, not as much.

  fun max(a, b)
  {
    if a > b
    {
      return a
    }
    return b
  }

Re: Avoid Else, Return Early (2013)

#309
post #293
post #271

Earlier quoted context omitted.

Huh? My emacs auto-indents both styles in exactly the same way: void foo() { code...; } void foo() { code...; }

The start of the code block is signified by the opening brace, which starts at the end of the method name in the first case, hence breaking symmetry.

To my eye, the start of the code block is signified by the indentation, i.e.:

    stuffstuffstuff....
      stuffstuffstuff...
So I read C and Python (and Lisp) code the same way. A naked open brace looks jarring and ugly to me. It also increases the separation of other related parts of the code, i.e.

    if (...) {
      while(...) {
        do(...) {
vs

    if (...)
    {
      while(...) {
      {
        do(...) {
The latter seems unnecessarily wasteful to me.

Re: Avoid Else, Return Early (2013)

#310

Earlier quoted context omitted.

Totally disagree with you.... it's funny though that I read LOC as "level of complexity" not "lines of code". I've been writing code for 30 years and I think it's jarring when the braces are on the next line, so much easier for me to parse that when it's on the same line. But everyone is entitled to their own opinion.

python fans are going like "what are braces?"

    >>> from __future__ import braces
    SyntaxError: not a chance
Post reply on HN