1

The finally block for a try-catch structure is well known, and gives a really easy and elegant way to deal with some must-be-done code.

Therefore, I can see no reason why It shouldn't be good for methods too. For instance, lets say I'm writing some very complicated logic in a method, and I expect to end up with a bunch of boolean flags that will in turn lead to some decisions. Many times in such kind of methods I have branches of code where I would want to "break" the flow and just perform the "real stuff" that I called this method for, with the satisfying information I gathered so far.

So, Why isn't there such pattern? Or is there?

7
  • 1
    Many languages have goto for that.
    – johannes
    Jun 20, 2013 at 22:14
  • 11
    Using "a bunch of boolean flags" is a red flag that you're approaching your logic incorrectly, and you will benefit from refactoring your approach to the control flow in question. This is also why there's no need for such a "pattern" on functions.
    – Matt D
    Jun 20, 2013 at 23:30
  • After getting the above answers (and some others in SO where the question was originally posted) I conclude that it could indeed be a good idea, and probably programmers fear from new concepts, or rather fear to admit that their favorite language is flawless... Thanks @johannes for pointing out the different way to achieve such behavior. Whould have vote up if I had the privilege.
    – Elist
    Jun 21, 2013 at 8:20
  • If I understand your question correctly you can simply put the decision logic in a separate function, and return when you want to "break the flow and perform the real stuff".
    – lortabac
    Jun 21, 2013 at 9:07
  • 1
    Cross-posted: stackoverflow.com/questions/17223949/…
    – user39685
    Jun 21, 2013 at 9:41

9 Answers 9

9

The first thing is:
Don't have too complex functions doing too complicated things.

If you can't avoid complex functions, there are various other tools to handle the situation.

In C and other languages people often use goto for the reason.

void func() {
    /* .. code .. */
    if (condition()) goto end;
    /* ... more code ...*/
end:
    cleanup();
}

Now some people dislike goto even in such cases, so they emulate goto using a do { ... } while(0) loop:

void func() {
    do {
        /* .. code .. */
        if (condition()) break;
        /* ... more code ...*/
    } while(0);
    cleanup();
}

Some languages, for instance PHP, allow giving a parameter to the break-keyword to jump multiple levels out:

function func() {
    do {
        /* .. code .. */
        if(something() {
            /* some code */
            if (condition()) break 2;
            /* ... */
        }
    /* ... more code ...*/
    } while(0);
    cleanup();
}

But there are languages with higher-level constructs. C++ for instance has destructors, there people use the RAII pattern:

struct cleanup {
    ~cleanup() {
        /* .. do cleanup ... */
    }
}

void func() {
    cleanup cleaner; // this stack object will be destroyed on
        // scope exit by calling the destructor
    if (condition()) return;
    /* ... more code ...*/
}

Some other languages have neither goto, nor nested break, nor destructors. Thus Java programmers have to live with nested if's, abuse exceptions, ... or make the code simpler.

public static void func() {
    try {
        /* .. code .. */
        if(something() {
            /* some code */
            if (condition()) throw new ControlFlowException();
            /* ... */
        }
        /* ... more code ...*/
    } catch (ControlFlowException e) {
        /* ignore */
    } finally {
        cleanup();
    }
}

So yes, alternative concepts exist and differ by language.

9
  • +1 for the wide and enlightening review of languages, but I'm still not convinced this shouldn't be a standard way to "destruct" methods. goto can make the code unreadable, and the other ways are just tricks.
    – Elist
    Jun 20, 2013 at 22:38
  • Yes, goto can be abused, so can be if clauses and for loops. Having clear distinct jumps in such situations is (imo) a proper use, though. On the others: Sure in a way they are hacks, but adding a specific language construct to languages makes the languages more complicated for the sake of a functionality that should be avoided by writing smaller functions in the first place.
    – johannes
    Jun 20, 2013 at 23:13
  • This cleanup pattern can also be implemented using two functions. int func_with_cleanup() { int exitcode = func(); if (!exitcode) cleanup(); return exitcode; } int func() { ... }
    – rwong
    Jun 21, 2013 at 6:39
  • 1
    Now some people don't like goto is a little understated, I guess.
    – Uooo
    Jun 21, 2013 at 8:37
  • 2
    That Java example should be removed or corrected. Java does have nested breaks. See the language specification.
    – COME FROM
    Sep 10, 2013 at 10:01
4

My guess is that there isn't a huge need for that sort of feature for two reasons:

  1. Most languages already give you pretty explicit flow control with looping and branching constructs.
  2. One of the most common uses of finally is to ensure resources are safely disposed. If your method is so complicated that you need this, you should probably refactor it into more manageable pieces.
2

It's worth noting that C# has the using statement which will automatically call Dispose() on the object when it leaves scope. This includes disposing of it after a return statement, so it acts like a finally in that respect.

Note that this is tied to the object, not tied to the function. But it does handle the main usecase for a finally in a function.

3
  • 1
    I don't know C# in that detail ... how does this compare to destructors in other languages?
    – johannes
    Jun 20, 2013 at 23:15
  • @johannes - See this SO answer. The short form is that the destructor is called at some arbitrary point when the object is garbage collected, but Dispose() should any managed resources right then.
    – Bobson
    Jun 21, 2013 at 5:44
  • Ah the wonders of Garbage Collection ... in C++ one knows when a destructor is called (explicit delete on heap, LIFO on stack) as does one i.e. with PHP when not using references (explicit unset or LIFO on function exit). Thanks for the link.
    – johannes
    Jun 21, 2013 at 9:58
2

The Go programming language has something similar to this idea: the defer statement. Deferred functions are called when the enclosing function exits for any reason, and can be method calls or plain functions, e.g.:

func fileProcessor(filename string) (err error) {
    var file *os.File
    if file, err = os.Open(filename); err != nil {
        return
    }
    defer func() {
        fmt.Printf("Closing file\n")
        file.Close()
    }()
    fmt.Printf("Processing file\n")
    return
}

In this case, the deferred function is anonymous. It could also just be:

defer file.Close()

But the anonymous routine allowed the Printfs in the example to highlight the order of execution.

In practice, I find defers to be a clean way to ensure the release of locally acquired resources. The approach isn't commonly found in other languages, but I find its presence in Go to be a welcome one, and would like to see it elsewhere.

1
  • It exists in D and ResourceT in Haskell Sep 17, 2013 at 1:31
1

Extract method sounds like a good plan: break up you method into an "initialization" function, a "do your thing" function and a "finalization" function. That way the finalization will always be done regardless of what happens in "do your thing". Except in the case of exceptions of course, but that is where the finally block of your languages comes in. You would then put the "finalization" function in the finally block.

procedure MainFunction;
begin
  InitializationFunction;
  try
    DoYourThingFunction;
  finally
    FinalizationFunction;
  end;
end;
0
1

finally causes a lot of code repetition. If you use a resource Foo in 100 places, and the cleanup of Foo takes 3 lines, then the finally blocks would add 300 lines of code but a destructor adds only 3 lines total.

5
  • Funny that you allude to recommending destructors in that the only language I know that has them is C++, which does NOT have finally blocks. Sep 16, 2013 at 21:41
  • 1
    I don’t get it. Why would this add the 3 lines 100 times?
    – gnasher729
    Jul 10, 2021 at 7:13
  • @gnasher729 Cleanup takes 3 lines * 100 places need it = 300 lines vs. Cleanup in destructor and using RAII = 3 lines. Elementary math. Try-with-resources, using-resource and the like were added to many such languages to mitigate the disadvantage of missing RAII though. Jul 10, 2021 at 19:35
  • Deduplicator: You only need one defer statement for one resource, and a compiler duplicating this statement would be brain dead.
    – gnasher729
    Jul 11, 2021 at 20:53
  • @gnasher729 defer is good for keeping one resource needing exceptional cleanup next to its cleanup, and for not duplicating that resource for multiple exits from the block. It is strictly worse than try-with-resources, using-resource, or especially RAII for the work those are designed for. Each of those must be used once for each place the resource is allocated. Aug 9, 2021 at 23:08
0

Does this really make your code more readable than alternatives? You could already put a try/finally around the code inside your function or you could put the "Finally" code right after the method is called.

Although it's a reasonable concept, every new feature you place into a language requires many many programmer hours of time to learn and understand--hundreds--thousands... If there is any way you can use an existing construct instead without causing a lot of duplication, boilerplate or confusion then it would be much better to use the existing mechanism.

3
  • I think such feature could be added without forcing any programmer to learn it or even know about it. Anyway, I don't think that confusing programmers should be a reason to not improving a language, when it doesn't break down the backward compatibility for already written code. programmers will get along, and new programmers will enjoy the benefit of a better language.
    – Elist
    Jun 21, 2013 at 8:18
  • Working with a team is different thatn working alone. You describe working alone (which is a perfectly valid scenario) but your assumptions don't apply to a team working on a largish project. If I code something using a new feature, my teammates (even overseas contractors) must know it (and there are always programmers anxious to try new language features, so it will happen). I think that when you are working alone you should just choose one of the many existing languages that have exciting, new constructs--you might be able to implement this feature in scala yourself, give it a try!
    – Bill K
    Jun 21, 2013 at 18:14
  • Thanks for the reference to scala. It looks interesting and I'll give it try. About working in team - I still think a reasonable, readable and self-explanatory feature will not be a problem.
    – Elist
    Jun 22, 2013 at 19:33
0

Some years later, several languages have a “defer” statement. Say you open a file, and you know you’ll need to close it eventually. So you write

Open file f
Defer close file f

The compiler guarantees that the defer statement will be called as late as possible. The good thing is that you can write the cleanup together with resource creation, at the point where the knowledge is there.

0

Actually, the TTCN-3 language was extended recently to support using finally and catch on function level. Link to the standard

For example:

testcase t_myTest1() runs on myComponent {
  f_init("user1");
  f_init("unknown user");// bad argument will raise an exception in f_init
  ... // because of the raised exception execution continues in the finally block
} finally {
   ... // freeing up resources
}

Testcases have to be independent, without any side effects on each other. At the same time, by their very purpose, they might uncover situations in the system under test, that might break their usual execution paths ... but the resources allocated for the test would still need to be freed.

As such it makes sense for the language to offer a syntax that lets test developers achieve their aim (of freeing the resources), without having to put the logic of each testcase/function/etc... in a try-catch-finally block, which would just push the actual main logic of the test/function +1 level deeper.

Not the answer you're looking for? Browse other questions tagged or ask your own question.