Post navigation

Why are braces required in try-catch-finally?

Developers who use C-like languages typically conceive of if, while, for, and so on as taking either a single statement, or a group of any number of statements in a block:

if (x)
M();

if (x)
{
M();
N();
}

However, that’s not how programming language designers think of it. Rather, if and while and for and so on each take a single statement, and a braced block is a single statement.[1. C# has an additional rule that the statement in an if, while and so on may not be a single local variable declaration; that’s a good subject for another day.]

No matter how we choose to think about the grammar, it is certainly the case that try-catch-finally is different than if and while and for and so on; try-catch-finally requires a braced block. That seems inconsistent; is there a justification for this inconsistency?

Before we dig into the try-catch-finally case, let’s first consider the problems with this approach. The looping structures are unambiguous:

while(A())
while(B())
C();

The inner while statement composes nicely with the outer while statement; no braces are required to make sense of this. But that is not the case with if, thanks to the famous “dangling else problem”:

if (A())
if (B())
C();
else
D();

OK, quick, is the indenting correct there? Is else D() associated with the inner if statement or the outer one?

It’s associated with the inner one; the else matches the nearest containing if. But in a language where whitespace doesn’t matter, it is very easy to accidentally indent this wrong and get the wrong impression when reading the code. I’ve also seen badly-written macros in C and C++ that caused the dangling-else problem to arise.

When adding try-catch-finally to the language, the designers wished to avoid adding a second kind of dangling else problem. Suppose that you could put any statement after a try, catch or finally, rather than having to put a block statement. How do you analyze this program fragment?

try
try
A();
catch AException
B();
catch BException
C();

OK, quick, is the indenting correct there? Is B() protected? That is, should we parse this as

Rather than attempt to come up with a rule to disambiguate the ambiguous parse, it is better to simply avoid the ambiguity altogether and require the braces. The last thing we need in this language is more ambiguity.

While we’re on the subject, an interesting thing about the try block is that of course the try keyword is completely unnecessary from a grammatical perspective. We could simply have said that any block can be followed by any number of catch blocks or a finally block, and the block thus followed is implicitly a try block. This is a good example of how enforcing redundancy into the language makes it more readable; the try keyword calls the reader’s attention to the fact that the control flow of this part of the method needs to deal with exceptional situations.

And one additional fun fact: in the initial design of C#, there was no such thing as try-catch-finally. There was try-catch and try-finally. If you wanted to have a try-catch-finally then you’d write:

try
{
try
{
A();
}
catch AException
{
B();
}
}
finally
{
C();
}

The language designers realized that this was a common pattern and unnecessarily wordy, so they allowed the syntactic sugar of eliminating the outer try. The C# compiler actually generates the code as though you’d written the nested blocks, since at the CIL level there is no try-catch-finally.

The underlying reason for not changing if/else isn’t necessarily because of compatibility with C. It’s because it is unnecessary: the “else” is grouped with the nearest “if”.

This is in contrast with catch; the most important part of the problem (which Eric pointed out) is that **catch can have multiple clauses**. His first example if try/catch nested blocks without braces would result in an invalid program if you followed the if/else guideline of grouping with the nearest try. With if/else, there is no chance of writing an invalid program (well, the program may not behave like you expect, but it compiles, so that’s something).

So in other words, the compilers just didn’t feel like taking that into account? (Fair enough, as it probably would mean a whole new pass through the code)

The way I see it, this is not ambiguous at all – one way is invalid code. Maybe that makes the language not context-free any more, but that doesn’t mean it’s ambiguous. Also I’m pretty sure there are other ways in which most programming languages are already not context-free, but I don’t remember.

Or maybe you mean that it’s ambiguous to the developer – he may have meant it one way and forgotten to put the catch at the end, and then it compiles wrong. If that’s what you mean, then you still can’t say that the language itself would be ambiguous. We write all kinds of confusing things that look like they mean one thing but actually mean another. Sure reducing these instances is good… But it doesn’t make the language ambiguous.

Kendall, the ambiguity is in the grammar, not the runtime behaviour. There are multiple valid ways to parse a dangling else, so there needs to be another rule about how to resolve the ambiguity. It’s famous enough to get its own Wikipedia entry – http://en.wikipedia.org/wiki/Dangling_else .

One relevant corner of C-based languages that tickles me is the fact that do / while does not require braces. It looks extremely odd without them though, e.g.:

Count me in the “Wishing braces were required” camp – but I know other developers (Miguel de Icaza for one, I suspect) who would have been up in arms about it.

The part about the initial design about C# does make me wonder why there isn’t a using/catch statement. I hardly ever write finally blocks in C#, as I’m almost always just disposing of a resource… but if I want to catch an exception in an area which also uses a resource, I *either* have to write a try/catch within the using statement, or I have to resort to just try/catch/finally. I realize using/catch looks odd *now*, because C# doesn’t have it… but I wonder whether it would have looked odd if it had been introduced to start with.

Another similar design question I’ve pondered before: given that the “as” operator is almost always followed by an “if” check, I wonder why there isn’t syntax for it. For example:

Doesn’t “using” automatically contain a “catch”? Or do you mean for additional tidy-up that’s not already inside the “dispose”?

I totally agree about as-if.

I could also do with some way of simplifying the pattern:
bool succeeded = DoSomething(data);
if(!succeeded) return false;
//Carry on to do next step.
which seems to litter any kind of processing that pulls together data from multiple sources, any one of which might fail.

As tempting as this looks, I’m sure there has to be some ambiguity somewhere that can’t be resolved in that construct. I’d be perfectly happy using a one-liner if we could transmute this:
Dog dog;

if ((dog = GetMeAnAnimal() as Dog) != null)
{
dog.Bark();
}
into:
if ((Dog dog = GetMeAnAnimal() as Dog) != null)
{
dog.Bark();
}
that way, the dog variable has local scope to the if block and the rest of it looks pretty straightforward.

Yes – it has at least two breaking changes that I can think of. If Animal has a method shadowed in Dog, the semantics change.

Also, if there were some “animal as Cat” line within the inner block, I believe this would now fail to compile. It would have been wrong before, but would have executed without an exception. This is better behaviour, but still technically a breaking change.

Although one could achieve the above effect by trying to cast thing to IEquatable(of T) and calling its Equals method if the cast succeeds, doing that would defeat the whole purpose of using IEquatable in the first place. For the exact scenario above, one could use EqualityComparer(of T).Default, but there’s no consistent pattern which could be used with all interface-implementing structs.

As far as “as-if” goes, it’s basically a simplified form of pattern matching, where the only pattern you can match is type. So it would make more sense for it to look more similar to a switch statement; e.g.:

typeswich (animal)
{
case Dog dog:
{
…
}
…
}

In fact, there is a bunch of feature requests on Connect for that already.

Your dictionary idea won’t work unless you only use sealed classes as your keys. If Dog is an abstract class, nothing will ever match it because actual objects will have type Beagle or Husky.

If you decided that you wanted to allow such scenarios by walking the whole Dictionary in cases when there’s no exact match, you run into the problem of the order not being defined. In reality you might want some behavior for Dog and, failing that, some other behavior for Animal. But since there’s no guarantee that your dictionary would enumerate Dog before Animal, your Dog case might never hit.

What you’d really have to do is create your own TypeSwitch class that simply took a list of delegates and enumerated them on every lookup. But even then you’d end up with delegates that couldn’t access local variables or having to create a complete set of closures every time you needed to do a lookup.

as-if seems to fail the bar required of language features.
a) It’s redundant, you can accomplish the same thing with only a few more characters, if((animal as Dog) != null) { }
b) It’s ultra specific to one use case.

a) It’s not quite the same thing, because in your example the type of animal still remains Animal – you can’t treat it as Dog inside the if body. In practice you need to introduce a new variable, and what’s worse, it pollutes the scope outside the if.

The F# still doesn’t have try … catch … finally (actually try .. with .. finally); instead you have to write the nested form; I’ve exchanged some mails with Luca when 1.0 was implemented, and while he admitted it’d be nice, nothing had happened until now I was given many technical details why it’s not possible — but I didn’t understand it, given the fact C#, C++, Python and others had it already. F# is crippled in many ways, and that’s a pity for it.

+1 for as-if pattern
+1 for ? for null checks as in “object?.property?.property?.property”

Great article, well put. We’ve rolled out “stylecop” verification in our projects and one of the requirements is always using braces for all the reasons you list here. Sure in some situations it might look less verbose without braces but you’ve only got to add them in the future if that “if” statement requires two lines, rather than one.

I’ve always found it strange that try…catch…finally are combined. Catch and finally don’t strike me as related. I use catch blocks to handle possible exceptional behavior. I use finally blocks to restore invariants at the end of the block. It’s nothing more than coincidence in my code when a catch block and a finally block apply to the same try block.

In designing a new language, what is the reason for not fixing the if/else problem? Is it simply a matter of “C worked this way when no-one knew better, and C++ and Java and JScript and Perl didn’t want to be the one to be different with superficially similar syntax, so C# won’t either”?

The problem is that adding even visual (especially vertical) code-bulk is just one more excuse not to include finer-grained exception-handling, or any at all. That and C# being a statement-orientated language and not a expression-orientated one.

Compare something like:

x = try compute() catch 0;

with:
try
{
x = compute();
}
catch
{
x= 0;
}

This is not a subtle savings at all and this lack of expressivness is a shame and makes programs unnecessarily hard to follow.

Coming from a PL/I and Pascal background I disliked the aesthetics of C# so when I started using ASP.Net it was more natural to me to use VB rather than C#. That said, after 7 years of using Visual Studio I have yet to find any reason other than aesthetic preference for preferring one language over the other. I would like to see Visual Studio make the languages just entry/display options, so that one could write code in either language within one project, even copy/paste C# into VB and vice versa, and have the IDE display it with your syntax of choice. C# users then could take advantage of the beauty of If … Then … [Else …] End If and Try … Catch … End Try where there is no issue about whether braces are necessary or not.

We have seen errors tied to “if” statements and other such constructs without braces, that my current team has taken to making it part of our standard to require braces around all if/else/loops/etc. Unfortunately, we’re rewriting an application in place (mostly, and which presents challenges best reserved for another topic) but the takeaway is that I don’t *think* it allows us to set up a rule of some sort without first having to fix all the pre-existing violations of the standard.

Are you using R#? I don’t have a copy handy right now, but I’m pretty sure you could use its find/replace features to find all the if/else/loops/etc… that need braces and add them.

I will say that our team required use of braces on all such statements, but I am strongly advocating against it now. I have some tendinitis in my right wrist, and any keystrokes that can be eliminated are good. Plus I find that unnecessary braces distract me by elongating code in the vertical direction. Screen space is finite, the more I can get on there the better. Besides, they eventually just become noise that you have to tune out.

Unfortunately, I know all too much about right wrist pain recently. However, as I mentioned, I also know all too much about bugs and a general lack of readability associated with missing braces or, even worse, mismatched braces. So we go with the standard. We also have standards advocating short methods and small classes. Screen space isn’t the issue we are worrying about, verifiability is.