39

In various languages (Java at least, think also C#?) you can do things like

if( condition )
    singleStatement;

while( condition )
    singleStatement;

for( var; condition; increment )
    singleStatement;

So when I have just one statement, I don't need to add a new scope with { }. Why can't I do this with try-catch?

try
    singleStatement;
catch(Exception e)
    singleStatement;

Is there something special about try-catch which requires always having a new scope or something? And if so, couldn't the compiler fix that?

Svish
  • 1,092
  • 8
  • 14
  • 3
    Nit-picking here: Strictly speaking for `for` the parts should be named something like `initial`, `condition` and `step` as `initial` need not define a variable and `step` need not be an increment. – Joachim Sauer Nov 03 '11 at 13:58
  • 4
    as a side note D doesn't require braces around the single statement try catch blocks – ratchet freak Nov 03 '11 at 14:07
  • 13
    I think the real question is the other way round: why do some constructs allow for "naked" statements? Braces should be compulsory everywhere for consistency. – UncleZeiv Nov 03 '11 at 18:51
  • A lot of coding errors could be avoided by requiring the braces in all the above situations. So I am with @UncleZeiv on this one. – Martin York Nov 03 '11 at 22:36
  • 3
    @UncleZeiv - it's not inconsistent if you consider that what follows the `if` is always a single statement, and multiple statements enclosed by braces comprise a single statement. But I'm one of those who always put the braces in anyway, so... – detly Nov 04 '11 at 03:24
  • 1
    I tend to write the braces as well, but I like not having to write them when I have direct exit points like `throw` and `return`, and like @detly said, if you consider the braces as a single group of statements, I don't find it inconsistent either. I've never understood what these "lots of coding errors" mentioned by people are. People need to start paying attention to what they do, use proper indention and have unit tests :P never had a problem with this... – Svish Nov 04 '11 at 07:18

4 Answers4

24

IMO, they're included in Java and C# primarily because they already existed in C++. The real question, then, is why is C++ that way. According to The Design and Evolution of C++ (§16.3):

The try keyword is completely redundant and so are the { } brackets except where multiple statements are actually used in a try-block or a handler. For example, it would have been trivial to allow:

int f()
{
    return g() catch(xxii) { // not C++
        error("G() goofed: xxii");
        return 22;
    };
}

However, I found this so difficult to explain that the redundancy was introduced to save support personnel from confused users.

Edit: As to why this would be confusing, I think one has only to look at the incorrect assertions in @Tom Jeffery's answer (and, especially, the number of up-votes it has received) to realize that there would be a problem. To the parser, this is really no different from matching elses with ifs -- lacking braces to force other grouping, all catch clauses would match up with the most recent throw. For those misbegotten languags that include it, finally clauses would do the same. From the viewpoint of the parser, this is hardly enough different from the current situation to notice -- in particular, as the grammars stand now, there's really nothing to group the catch clauses together -- the brackets group the statements controlled by the catch clauses, not the catch clauses themselves.

From the viewpoint of writing a parser, the difference is almost too tiny to notice. If we start with something like this:

simple_statement: /* won't try to cover all of this */
                ;

statement: compound_statement
         | simple_statement
         ;

statements: 
          | statements statement
          ;

compound_statement: '{' statements '}'

catch_arg: '(' argument ')'

Then the difference would be between:

try_clause: 'try' statement

and:

try_clause: 'try' compound_statement

Likewise, for catch clauses:

catch_clause: 'catch' catch_arg statement

vs.

catch_clause: 'catch' catch_arg compound_statement

The definition of a complete try/catch block would not need to change at all though. Either way it would be something like:

catch_clauses: 
             | catch_clauses catch_clause
             ;

try_block: try_clause catch_clauses [finally_clause]
         ;

[Here I'm using [whatever] to indicate something optional, and I'm leaving out the syntax for a finally_clause since I don't think it has any bearing on the question.]

Even if you don't try to follow all the Yacc-like grammar definition there, the point can be summarized fairly easily: that last statement (starting with try_block) is the one where catch clauses get matched up with try clauses -- and it remains exactly the same whether the braces are required or not.

To reiterate/summarize: the braces group together the statements controlled by the catchs, but do not group the catchs themselves. As such, those braces have absolutely no effect upon deciding which catch goes with which try. For the parser/compiler the task is equally easy (or difficult) either way. Despite this, @Tom's answer (and the number of up-votes it's received) provides ample demonstration of the fact that such a change would almost certainly confuse users.

Jerry Coffin
  • 44,385
  • 5
  • 89
  • 162
  • The OP is asking about the brackets around both the try and the *catch block*, while this appears to be referencing those around the *try* (the first paragraph could be understood to reference both, but the code illustrates only the former)... Can you clarify? – Shog9 Nov 03 '11 at 18:27
  • @Mr.CRT: a "catch block" would otherwise be known as a "handler", about which see the quote above. – Jerry Coffin Nov 03 '11 at 18:32
  • 3
    bah, just edited my comment to remove *that* ambiguity. What I'm getting at is that this could be a more effective answer than Tom's (above) if it went to greater lengths to illustrate how the bracket-less construct *could* have worked, but in a confusing manner (Billy's comment on Tom's answer seems to imply that it *could not* have worked, which I believe is incorrect). – Shog9 Nov 03 '11 at 18:35
  • @JerryCoffin Thanks for expanding your answer: it's much clearer to me now. –  Nov 03 '11 at 20:30
  • `try return g(); catch(xxii) error("G() goofed: xxii");` whould have been neat still IMO – alfC Dec 17 '14 at 01:55
19

In an answer about why brackets are required for some single-statement constructs but not others, Eric Lippert wrote:

There are a number of places where C# requires a braced block of statements rather than allowing a "naked" statement. They are:

  • the body of a method, constructor, destructor, property accessor, event accessor or indexer accessor.
  • the block of a try, catch, finally, checked, unchecked or unsafe region.
  • the block of a statement lambda or anonymous method
  • the block of an if or loop statement if the block directly contains a local variable declaration. (That is, "while (x != 10) int y = 123;" is illegal; you've got to brace the declaration.)

In each of these cases it would be possible to come up with an unambiguous grammar (or heuristics to disambiguate an ambiguous grammar) for the feature where a single unbraced statement is legal. But what would the point be? In each of those situations you are expecting to see multiple statements; single statements are the rare, unlikely case. It seems like it is not realy worth it to make the grammar unambiguous for these very unlikely cases.

In other words, it was more expensive for the compiler team to implement it than was justified, for the marginal benefit it would provide.

Robert Harvey
  • 198,589
  • 55
  • 464
  • 673
13

I think it's to avoid dangling else style issues. The following would be ambiguous...

try
    // Do stuff
try
    // Do  more stuff
catch(MyException1 e1)
    // Handle fist exception
catch(MyException2 e2)
    // So which try does this catch belong to?
finally
    // and who does this finally block belong to?

It could mean this:

try {
   try {

   } catch(Exception e1) {

   } catch(Exception e2) {

   } 
} finally {

} 

Or...

try {
   try {

   } catch(Exception e1) {

   } 
} catch(Exception e2) {

} finally {

} 
Tom Jefferys
  • 303
  • 1
  • 4
  • 25
    This ambiguity applies to *if* and *for* equally. The question is : Why is it that it is allowed for *if* and *switch* statement but not for *try/catch*? – Dipan Mehta Nov 03 '11 at 14:49
  • 3
    @Dipan fair point. I wonder if it's simply a matter of Java/C# trying to be consistent with older languages such as C by allowing non-braced ifs. Whereas try/catch is a newer construct, therefore the language designers thought it ok to break with tradition. – Tom Jefferys Nov 03 '11 at 15:37
  • I tried answering the compulsion at least for C and C++. – Dipan Mehta Nov 03 '11 at 15:41
  • 6
    @DipanMehta: Because there are not multiple possible dangling else clauses for the `if` case. It's easy to just say "well the else binds to the innermost if" and be done with it. But for try/catch that doesn't work. – Billy ONeal Nov 03 '11 at 16:07
  • 2
    @Billy: I think you're glossing over the potential ambiguity present in if / if else... It's easy to say "it's easy to just say" - but that's because there's a hard rule for resolving the ambiguity that, incidentally, disallows certain constructs without the use of brackets. Jerry's answer implies this *was* a conscious choice made to avoid confusion, but surely it *could* have worked - just as it "works" for if / if else. – Shog9 Nov 03 '11 at 18:38
  • @Mr.CRT: It's a lot easier to distinguish between two possible interpretations in a well defined manner than to distinguish between 2^n possible interpretations in a well defined manner. Sure, you could define try/catch semantics to work like if/else. But I doubt you can do it in such a way that it appears consistent to most readers. – Billy ONeal Nov 03 '11 at 19:08
  • @Billy: which is a pretty good argument for doing it this way - force the *writer* to explicitly disambiguate the construct. This is the same argument often made for bracketing nested if/else statements though, even though the compiler doesn't *require* it. – Shog9 Nov 03 '11 at 19:44
  • @BillyONeal So if a language included some sort of elseif/elsif construct, ifs should also always have {}? – luiscubal Nov 03 '11 at 23:47
  • @luiscubal: No. Was just saying it's easier to write a consistent rule when there are only two alternatives, which is why if doesn't need braces, and try/catch does. – Billy ONeal Nov 04 '11 at 00:14
  • @Billy ONeal: elsif(note I did *not* say `else if` separated) would introduce multiple alternatives, just like trys can have multiple catches. So wouldn't that be the exact same problem for the compiler? (assuming no final `else` statement) – luiscubal Nov 04 '11 at 15:20
  • @luiscubal `elsif` / `elseif` are the exact same as else followed by an if in terms of semantics. Therefore, the same rules that work for else would work for elsif or elseif. – Billy ONeal Nov 04 '11 at 15:22
  • @Billy ONeal: Let's suppose there's a language where plain else does not exist(only elsif). In that language, elsif is pretty much the same as catch. try catch catch, if elsif elsif. try try catch catch, if if elsif elsif. Compiler design complexity is the exact same. – luiscubal Nov 04 '11 at 17:38
1

I think the main reason is that there is very little you can do in C# that would need a try/catch block that is only one line. (I can't think of any right now of the top of my head). You may have a valid point in terms of the catch block, e.g a one line statement to log something but in terms of readability it makes more sense (at least to me) to require {}.

Jetti
  • 5,163
  • 2
  • 26
  • 41