www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Suggestion: Syntactic sugar for Exception handling in D2

reply Ulrik Mikaelsson <ulrik.mikaelsson gmail.com> writes:
One thing I often encounter in D, and other languages, is functions looking
something like;

void myfunc(Obj input)
{
   try {
     do_something(input);
   } catch (SomeException e) {
     handle_error(e);
   }
}

While there's no real problem with this code, I have some experience from Ruby,
which has added some syntactic sugar regarding this, making all code-blocks a
potential "try-clause", if there's a catch-block (or finally).

In D, it would look something like (and have the exact same semantic meaning of
the code above);

void myfunc(Obj input)
{
   do_something(input);
}
catch (SomeException e)
{
   handle_error(e);
}

IMHO, this syntactic addition gives a few advantages;
  * Makes the code slightly more readable, since the "exceptional" code-paths
are clearly separated
  * Biases me as a programmer to think a little bit more of exactly what
exceptions can be raised in a function, improving my code-quality.
  * When I'm about to write a try-clause, makes me think twice if the code
could not be extracted as a separate method instead (if I can only figure a
good name for it), also improving readability and code-structure.

To sum up; while this is purely syntactic sugar, my personal experience from
Ruby is that this syntax encourages better coding on my part, which I think
would be a good thing to incorporate in D.

One thing, I'm pondering though, is exactly in what blocks this should be
allowed, and what semantics should apply.
  * Inner anonymous functions? 
  * If statements?
  * For-loops? If so, is the try for the entire loop, or per iteration?
  * How does this relate to the contract-programming-features in D?

Comments / opinions on this, anyone?
Jun 21 2009
next sibling parent reply =?ISO-8859-1?Q?Tomasz_Sowi=f1ski?= <tomeksowi.get.rid of.gmail.unnecessary.com.stuff> writes:
I like it. Although it's only sugar you're right -- it helps reading a bit. But
I would allow (or even require) "try" before a block of code:

if (condition) try {
    ...
} catch (Exception ex) {
    ...
}

Seeing "try" there cuts down on the gray matter needed to understand what it
does. And just not to stray too much from the C-family.

Tomek

Ulrik Mikaelsson Wrote:

 One thing I often encounter in D, and other languages, is functions looking
something like;
 
 void myfunc(Obj input)
 {
    try {
      do_something(input);
    } catch (SomeException e) {
      handle_error(e);
    }
 }
 
 While there's no real problem with this code, I have some experience from
Ruby, which has added some syntactic sugar regarding this, making all
code-blocks a potential "try-clause", if there's a catch-block (or finally).
 
 In D, it would look something like (and have the exact same semantic meaning
of the code above);
 
 void myfunc(Obj input)
 {
    do_something(input);
 }
 catch (SomeException e)
 {
    handle_error(e);
 }
 
 IMHO, this syntactic addition gives a few advantages;
   * Makes the code slightly more readable, since the "exceptional" code-paths
are clearly separated
   * Biases me as a programmer to think a little bit more of exactly what
exceptions can be raised in a function, improving my code-quality.
   * When I'm about to write a try-clause, makes me think twice if the code
could not be extracted as a separate method instead (if I can only figure a
good name for it), also improving readability and code-structure.
 
 To sum up; while this is purely syntactic sugar, my personal experience from
Ruby is that this syntax encourages better coding on my part, which I think
would be a good thing to incorporate in D.
 
 One thing, I'm pondering though, is exactly in what blocks this should be
allowed, and what semantics should apply.
   * Inner anonymous functions? 
   * If statements?
   * For-loops? If so, is the try for the entire loop, or per iteration?
   * How does this relate to the contract-programming-features in D?
 
 Comments / opinions on this, anyone?

Jun 21 2009
next sibling parent =?ISO-8859-1?Q?Tomasz_Sowi=f1ski?= <tomeksowi.get.rid of.gmail.unnecessary.com.stuff> writes:
Oh God, the example already compiles in D (and a few other languages, I guess).
I'm sorry.

Tomasz Sowiński Wrote:

 I like it. Although it's only sugar you're right -- it helps reading a bit.
But I would allow (or even require) "try" before a block of code:
 
 if (condition) try {
     ...
 } catch (Exception ex) {
     ...
 }
 
 Seeing "try" there cuts down on the gray matter needed to understand what it
does. And just not to stray too much from the C-family.
 
 Tomek
 
 Ulrik Mikaelsson Wrote:
 
 One thing I often encounter in D, and other languages, is functions looking
something like;
 
 void myfunc(Obj input)
 {
    try {
      do_something(input);
    } catch (SomeException e) {
      handle_error(e);
    }
 }
 
 While there's no real problem with this code, I have some experience from
Ruby, which has added some syntactic sugar regarding this, making all
code-blocks a potential "try-clause", if there's a catch-block (or finally).
 
 In D, it would look something like (and have the exact same semantic meaning
of the code above);
 
 void myfunc(Obj input)
 {
    do_something(input);
 }
 catch (SomeException e)
 {
    handle_error(e);
 }
 
 IMHO, this syntactic addition gives a few advantages;
   * Makes the code slightly more readable, since the "exceptional" code-paths
are clearly separated
   * Biases me as a programmer to think a little bit more of exactly what
exceptions can be raised in a function, improving my code-quality.
   * When I'm about to write a try-clause, makes me think twice if the code
could not be extracted as a separate method instead (if I can only figure a
good name for it), also improving readability and code-structure.
 
 To sum up; while this is purely syntactic sugar, my personal experience from
Ruby is that this syntax encourages better coding on my part, which I think
would be a good thing to incorporate in D.
 
 One thing, I'm pondering though, is exactly in what blocks this should be
allowed, and what semantics should apply.
   * Inner anonymous functions? 
   * If statements?
   * For-loops? If so, is the try for the entire loop, or per iteration?
   * How does this relate to the contract-programming-features in D?
 
 Comments / opinions on this, anyone?


Jun 21 2009
prev sibling parent reply Ulrik Mikaelsson <ulrik.mikaelsson gmail.com> writes:
Tomasz Sowiński Wrote:

 I like it. Although it's only sugar you're right -- it helps reading a bit.
But I would allow (or even require) "try" before a block of code:
 
 if (condition) try {
     ...
 } catch (Exception ex) {
     ...
 }

foreach (item; collection) try { process(item); } catch (StopException e) { break; }
Jun 21 2009
parent Robert Fraser <fraserofthenight gmail.com> writes:
Ulrik Mikaelsson wrote:
 Tomasz Sowiński Wrote:
 
 I like it. Although it's only sugar you're right -- it helps reading a bit.
But I would allow (or even require) "try" before a block of code:

 if (condition) try {
     ...
 } catch (Exception ex) {
     ...
 }

foreach (item; collection) try { process(item); } catch (StopException e) { break; }

As Tomaz pointed out, this already works for statements (if, while, for, foreach, with, synchronized, etc.).
Jun 21 2009
prev sibling next sibling parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
I like the idea from a DRY perspective.  That said, it's a bit
problematic in that you can't tell if any given block has an exception
handler until you find the end of it and check.

As much as it looks like a nice simplification, I'm not sure this has
enough benefit to be worth it.
Jun 21 2009
next sibling parent reply Michiel Helvensteijn <m.helvensteijn.remove gmail.com> writes:
Daniel Keep wrote:

 I like the idea from a DRY perspective.

How so? I don't see anything 'repeated' in the before-case. -- Michiel Helvensteijn
Jun 21 2009
parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
Michiel Helvensteijn wrote:
 Daniel Keep wrote:
 
 I like the idea from a DRY perspective.

How so? I don't see anything 'repeated' in the before-case.

The 'try' keyword is redundant. The presence of any number of catches or a finally indicates that the block must trap exceptions. If there are no catches or a finally, then a try by itself would be pointless.
Jun 21 2009
next sibling parent Michiel Helvensteijn <m.helvensteijn.remove gmail.com> writes:
Daniel Keep wrote:

 I like the idea from a DRY perspective.

How so? I don't see anything 'repeated' in the before-case.

The 'try' keyword is redundant. The presence of any number of catches or a finally indicates that the block must trap exceptions. If there are no catches or a finally, then a try by itself would be pointless.

Ah yes. So the intention to trap exceptions is repeated. If there were no longer any exceptions to worry about, you would have to remove the "try" keyword as well as the "catch" clauses. I hadn't considered the DRY abbreviation to fit there, but now I see how it could. A bit obscurely, I guess, but still validly. -- Michiel Helvensteijn
Jun 22 2009
prev sibling parent Michel Fortin <michel.fortin michelf.com> writes:
On 2009-06-21 21:14:53 -0400, Daniel Keep <daniel.keep.lists gmail.com> said:

 Michiel Helvensteijn wrote:
 Daniel Keep wrote:
 
 I like the idea from a DRY perspective.

How so? I don't see anything 'repeated' in the before-case.

The 'try' keyword is redundant. The presence of any number of catches or a finally indicates that the block must trap exceptions. If there are no catches or a finally, then a try by itself would be pointless.

I disagree that 'try' is redundent. I say it resolves an ambiguity. For instance, it makes a big difference here depending on where you write your 'try': foreach (a; b) try { ... } catch (Exception e) { ... } vs. try foreach (a; b) { ... } catch (Exception e) { ... } With proper indentation and some unnecessary braces removed, it's basically this: try foreach (a; b) ...; catch (Exception e) ...; vs. foreach (a; b) try ...; catch (Exception e) ...; Repeat for other control structures ('while', 'if', 'else') and you'll find that the only place where 'try' can be elided is for standalone blocks not part of any control structure, and for the function body. Also, not all braces create blocks: 'static if', 'version' and 'debug' don't create a block even with braces, so you couldn't add catch after these. I think that consistently requiring 'try' is clearer than making it optional only in some circumstances. That said (throwing another idea into the discussion), I wouldn't be against making braces optional around a function body with only one statement: int f(int i) return i+1; in which case you could also write: int f(int i) try return i+1; catch (Exception e) return 0; -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Jun 22 2009
prev sibling next sibling parent Ulrik Mikaelsson <ulrik.mikaelsson gmail.com> writes:
To be honest, while the only obvious benefit is shorting down the number of
braces slightly, the real benefit I experienced from practicing it in Ruby were
a slight change in how I actually think about exception handling.

It's very hard to quantify how much benefit it really is, but I agree on that
it will make the parsing a bit backwards. If it's a big hassle I don't think
it's worth it, but if it can be added easily in some already-existing phase, I
think it should be seriously considered.

Daniel Keep Wrote:
 I like the idea from a DRY perspective.  That said, it's a bit
 problematic in that you can't tell if any given block has an exception
 handler until you find the end of it and check.
 
 As much as it looks like a nice simplification, I'm not sure this has
 enough benefit to be worth it.

Jun 21 2009
prev sibling parent Jarrett Billingsley <jarrett.billingsley gmail.com> writes:
On Sun, Jun 21, 2009 at 3:48 PM, Ulrik
Mikaelsson<ulrik.mikaelsson gmail.com> wrote:
 To be honest, while the only obvious benefit is shorting down the number of
braces slightly, the real benefit I experienced from practicing it in Ruby were
a slight change in how I actually think about exception handling.

If it's braces you're concerned with, D doesn't actually require them on try/catch/finally like many other languages: try doSomething(input) catch(Exception e) handleError(e);
Jun 21 2009
prev sibling next sibling parent reply Ulrik Mikaelsson <ulrik.mikaelsson gmail.com> writes:
I just remembered another tiny benefit of allowing this. Being a VCS-fascist, I
really strive towards patches that are as readable as possible.

   * Makes the code slightly more readable, since the "exceptional" code-paths
are clearly separated
   * Biases me as a programmer to think a little bit more of exactly what
exceptions can be raised in a function, improving my code-quality.
   * When I'm about to write a try-clause, makes me think twice if the code
could not be extracted as a separate method instead (if I can only figure a
good name for it), also improving readability and code-structure.

Jun 21 2009
parent Jarrett Billingsley <jarrett.billingsley gmail.com> writes:
On Sun, Jun 21, 2009 at 3:51 PM, Ulrik
Mikaelsson<ulrik.mikaelsson gmail.com> wrote:
 I just remembered another tiny benefit of allowing this. Being a VCS-fasc=

 =A0 * Makes the code slightly more readable, since the "exceptional" cod=


 =A0 * Biases me as a programmer to think a little bit more of exactly wh=


 =A0 * When I'm about to write a try-clause, makes me think twice if the =


ure a good name for it), also improving readability and code-structure.
 + =A0 * Patches adding exception-handling will not change indentation of =


Not to deflate you, but most respectable source diff tools I've seen have an option to ignore whitespace/indentation changes :\
Jun 21 2009
prev sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
I suggest looking at D's scope guard statements, which replace most uses 
of try statements.

http://www.digitalmars.com/d/1.0/statement.html#ScopeGuardStatement
Jun 21 2009
parent Max Samukha <outer space.com> writes:
On Sun, 21 Jun 2009 13:14:17 -0700, Walter Bright
<newshound1 digitalmars.com> wrote:

I suggest looking at D's scope guard statements, which replace most uses 
of try statements.

http://www.digitalmars.com/d/1.0/statement.html#ScopeGuardStatement

I don't think the example provided by the original poster can be implemented with scope guards because it's impossible to access the exception object in scope(failure).
Jun 21 2009