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

try
{
  try
  {
    A();
  }
  catch AException
  {
    B(); // protected by the outer try
  }
}
catch BException
{
  C();
}

Or is it this erroneous program?

try // try without associated catch!
{
  try
  {
    A();
  }
  catch AException
  {
    B(); // not protected
  }
  catch BException
  {
    C();
  }
}

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.


Eric is on vacation; this posting was pre-recorded.


Next time on FAIC: What happens when unmanaged code holds on to a fixed pointer? Nothing good.

74 thoughts on “Why are braces required in try-catch-finally?

  1. This is just begging the question. Surely this is ambiguous as well?

    bool foo = true;
    bool bar = false;
    bool baz = true;
    if (foo)
    if (bar)
    Console.WriteLine(“a”);
    else if (baz)
    Console.WriteLine(“b”);

    • It is, and is mentioned above — but the grammar for if/else has existed for a long, long time and changing it would break a good number of programs.

      The grammar for try/catch/finally took what they’d learned from nested ifs into account, in order to not run into the same problem.

      • Since C# was a new language, I don’t understand why it had to carry over every minute detail of C (in fact, it doesn’t). It’s not like C# had to compile without any change any C program.

        So, I don’t understand the logic of why If Else doesn’t require braces but Try Catch Finally does. You could have said If Else requires braces too. It was a new language.

        • 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.

          • Perhaps this change will highlight the ambiguity to you Nacht.

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

            Now either try can have 2 catches.

  2. 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.:

    do
    while(false); // nested while loop
    while(false); // end of do-loop

    • So the Delphi equivalent

      repeat

      do while true

      util true;

      is clearer as it uses difeerent keywords for the two looping constructs 🙂

    • You can enforce this rule using stylecop… You can configure projects so that they won’t even build if braces are ommited (as opposed to warnings)

  3. 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:

    Animal animal = GetMeAnAnimal();
    as-if (Dog dog = animal as Dog)
    {
    ..
    }

    I’d love a better name, but it does feel like it would be a useful construct.

    • 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.

    • There’s a better feature here that some languages have adopted and I would love to see in C#:

      Animal animal = GetMeAnAnimal();
      if (animal is Dog)
      {
      // animal now has type Dog here without any
      // casting (inferred by the compiler).
      animal.Bark();
      }

      Since the only way the inner block can be executed is if animal were Dog. So just treat it as such inside.

      • 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.

        • So, if

          if ((Dog dog = GetMeAnAnimal() as Dog) != null)
          {
          dog.Bark();
          }

          i think would make dog a nullable type, “as” returns null if the conversion fails.

    • You can do it almost in one step:

      Animal animal = GetMeAnAnimal();
      Dog dog;
      if ((dog = animal as Dog) != null)
      {
      ..
      }

      How about this?
      Animal animal = GetMeAnAnimal();
      with (Dog dog = animal as Dog)
      {
      ..
      }

      • Another one you can use now:

        Animal animal = GetMeAnAnimal();
        for (Dog dog = animal as Dog; dog != null; dog = null)
        {
        ..
        }

        I wouldn’t recomend it as it can easily become a dead lock if one forget’s the “dog=null” part.

        • I would rather suggest a more general if with declaration part:

          Animal animal = GetAnimal();
          if(Dog dog = animal as Dog; dog != null){
          }

          It’s more general because then you can do something like:

          if(Dog dog = animal as Dog; dog != null && dog.IsBlack){
          }

          Or even

          if(DayOfWeek dow = DateTime.Today.DayOfWeek; dow !=DayOfWeek.Saturday && dow !=DayOfWeek.Sunday)
          Console.WriteLine(“work!, its “+ dow);

          This synthax could also work with while loops, making an smooth transition to for loops.

          while(int read = file.Read(buffer); read > 0)
          file2.Write(buffer, read);

          This alternative has the advantage of being consistent with for synthax, be general, works for whiles and introduce no new tokens, but its slightly longer.

          IMO the most important thing is that the variable scope is inside-outside the if, preventing bugs.

          • I like the word ‘synthax’, but it could be extended to ‘synthrax’ – a mix of ‘syntax’ and ‘anthrax’ for any new construct that is deadly.

    • This is as close as I came to solve that.

      static bool AsIf(out TTarget target, TBase baseInstance) where TTarget : class
      {
      target = baseInstance as TTarget;
      return target != null;
      }

      void Test()
      {
      Dog dog;

      if (AsIf(out dog, GetMeAnAnimal()))
      {
      // That’s a dog!
      }
      }

      • Using a lambda you can do it with slightly less typing at the call site:

        static void AsIf(TBase source, Action action) where TChild : class, TBase
        {
        if (source is TChild)
        action((TChild)source);
        }

        void Test()
        {
        AsIf(GetMeAnAnimal(), (Dog dog) =>
        {
        dog.Bark();
        });
        }

        I still far prefer the cleaner type deduction I mentioned above though.

          • And my angle brackets got eaten. Let’s try again:

            private static void AsIf<TBase, TChild>(TBase source, Func<TBase, TChild> action) where TChild : class, TBase
            {
            if (source is TChild)
            {
            action((TChild)source);
            }
            }

          • Third time’s the charm (?):

            private static void AsIf<TBase, TChild>(TBase source, Action<TChild> action) where TChild : class, TBase
            {
            if (source is TChild)
            {
            action((TChild)source);
            }
            }

            allows for:

            AsIf(GetMeAnAnimal(), (Dog dog) => dog.Bark());

    • The one thing you’d need to include in your “as-if” is the “else” part: what if animal is a Cat?

      For completeness you’d often want to include “as-if-else” so that you can have:

      as-if (Dog dog = animal as Dog) { … }
      as-else as-if (Cat cat = animal as Cat { … }
      as-else { // animal isn’t Cat or Dog! }

    • I like the as-if construct (combined with an else), and would add that it could be particularly helpful in cases involving generics types which could be structures. For example,

      as-if (thing is Equatable(of T))
      {
      return thing.Equals(other); // IEquatable.Equals(T);
      }
      else
      {
      return thing.Equals(other); // Object.Equals(Object);
      }

      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.

    • A better, or possibly more consistent, way to deal with “as-if” would be to demote variable declarations to expressions – allowing you to:

      if((var dog = animal as Dog) != null)
      {

      }

      You might argue that it looks a bit ugly, but so does “while ((data = reader.ReadLine()) != null) {…}”.

      • A expression declared variable should be visible after the expression, or just inside?.

        For example, how many declared variables are here:
        var number = isSunday? (var a = 2) : (var b = 3);

    • 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.

      • Ah, Oberon’s WITH reintroduced 🙂

        And since I hate switches and favour lookups, let’s try to cook something different…

        var animalActions = new Dictionary< Type, Action > () {
        { typeof(Dog), (Dog dog) => { … } },

        }

        try {
        animalActions[animal.GetType()](animal);
        }
        catch (KeyNotFoundException) {
        // default action
        }

        Still ugly, slow, and error-prone 😦

        • 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.

          • Notice that all issues from you second paragraph apply to hypothetical TYPESWITCH control structure too: what if you mess up the order of CASE clauses in it?

    • 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.

        b) It’s a very, very common use case.

  4. If there was a “light syntax” as there is in F#, and the same as in Python, identation would solve the problems.

    This
    if (bool1)
    if (bool2)
    C()
    else
    D()
    else
    E()

    would be different from this:
    if (bool1)
    if (bool2)
    C()
    else
    D()
    /* the following (commented) wouldn’t even be allowed
    else
    E()
    */
    And thus, the ambiguation would disappear.

    • 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”

      • F# is growing, so I think sooner or later, it think (and hope) it will have every and more features than C#, C++ and Python together.

  5. Pingback: The Morning Brew - Chris Alcock » The Morning Brew #1247

  6. 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.

  7. 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.

    • var dbResource = new DBResource();
      try
      {
      return dbResource.ReadSomeData();
      }
      catch (Exception e)
      {
      LogException(e);
      }
      finally
      {
      dbResource.CleanUp();
      }

      🙂

  8. Overlooked in this — the more common place which requires a braced-block, even if just one statement — a method definition. i.e., you can’t write:

    int GetRandomNumber()
    return 4;

    It has to be

    int GetRandomNumber()
    {
    return 4;
    }

  9. Pingback: Why are braces required in try-catch-finally? | Fabulous Adventures In Coding | News de la semaine .net | Scoop.it

  10. 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”?

  11. 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.

    • Of course both the indentation-not-matching-semantics issue (with significant whitespace) and the expression issue are both addressed in F#.

  12. 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.

  13. 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.

        • With ReSharper you can do this as a code formatting style preference. Then it is a single shortcut or menu item to reformat the code in a file/project/solution.

  14. Pingback: Friday Links #232 | Blue Onion Software *

  15. Pingback: Liens de la semaine #7 « frenchcoding

  16. Re-reading your old blogs, I see you using
    “There are no stupid questions, only stupid people.”
    comment regularly. I prefer a different variant:
    “There are no stupid questions, only stupid answers.”
    😉

  17. So I was away from your blog for a month, came back and you are no longer with Microsoft! What gives?!

    Best wishes in your new role and Happy Holidays!

    I thoroughly enjoy reading your blog and even quoted one of your posts in a recent job interview!

  18. Pingback: WiFi Horizon, Best news updates, offers and business opportunities

  19. Pingback: Fabulous adventures | Fabulous adventures in coding

  20. Pingback: Brazilian Navy says it will sink &apos;ghost&apos; aircraft carrier at high sea – World Taxi and Tourism Blog

Leave a comment