Thread overview |
---|
September 05, 2016 TryElseExpression DIP | ||||
---|---|---|---|---|
| ||||
I was writing some code and realized D doesn't have an equivalent to Python's `else` for error handling, and I think we should change that https://github.com/dlang/DIPs/pull/43/files In Python, the try/catch/finally syntax is augmented with an additional clause, termed else. It is a fantastically useful addition to the conventional syntax. It works like this: try: do_something() except Exception as e: pass # Runs when an error inheriting from Exception was raised else: pass # Runs when no error was raised finally: pass # Runs unconditionally, evaluates last Imitating this functionality in D, try{ do_a_thing(); }catch(Exception exception){ handle_error(); }else{ depends_on_success_of_thing(); }finally{ do_this_always(); } Would be equivalent to bool success = false; try{ do_a_thing(); success = true; }catch(Exception exception){ handle_error(); }finally{ try{ if(success){ depends_on_success_of_thing(); } }finally{ do_this_always(); } } |
September 05, 2016 Re: TryElseExpression DIP | ||||
---|---|---|---|---|
| ||||
Posted in reply to pineapple | On 09/05/2016 08:07 PM, pineapple wrote:
> try{
> do_a_thing();
> }catch(Exception exception){
> handle_error();
> }else{
> depends_on_success_of_thing();
> }finally{
> do_this_always();
> }
>
> Would be equivalent to
>
> bool success = false;
> try{
> do_a_thing();
> success = true;
> }catch(Exception exception){
> handle_error();
> }finally{
> try{
> if(success){
> depends_on_success_of_thing();
> }
> }finally{
> do_this_always();
> }
> }
Can you point out how this is different from (and better than)
try { do_a_thing(); depends_on_success_of_thing(); }
catch (...) { ... }
finally { ... }
?
|
September 05, 2016 Re: TryElseExpression DIP | ||||
---|---|---|---|---|
| ||||
Posted in reply to pineapple | On Monday, 5 September 2016 at 18:07:52 UTC, pineapple wrote:
> It works like this:
>
> try:
> do_something()
> except Exception as e:
> pass # Runs when an error inheriting from Exception was raised
> else:
> pass # Runs when no error was raised
> finally:
> pass # Runs unconditionally, evaluates last
>
> Imitating this functionality in D,
>
> try{
> do_a_thing();
> }catch(Exception exception){
> handle_error();
> }else{
> depends_on_success_of_thing();
> }finally{
> do_this_always();
> }
>
> Would be equivalent to
>
> bool success = false;
> try{
> do_a_thing();
> success = true;
> }catch(Exception exception){
> handle_error();
> }finally{
> try{
> if(success){
> depends_on_success_of_thing();
> }
> }finally{
> do_this_always();
> }
> }
hm, isn't this similar/same as the python version?
try{
doSomething();
scope(success) "if no Exception thrown".writeln;
}
catch(Exception e)
{
...
}
|
September 05, 2016 Re: TryElseExpression DIP | ||||
---|---|---|---|---|
| ||||
Posted in reply to ag0aep6g | On Monday, 5 September 2016 at 18:27:44 UTC, ag0aep6g wrote: > Can you point out how this is different from (and better than) > > try { do_a_thing(); depends_on_success_of_thing(); } > catch (...) { ... } > finally { ... } > > ? On Monday, 5 September 2016 at 18:27:52 UTC, arturg wrote: > hm, isn't this similar/same as the python version? > > try{ > doSomething(); > > scope(success) "if no Exception thrown".writeln; > } > catch(Exception e) > { > ... > } In this case, the catch block will catch both errors from do_a_thing and depends_on_success_of_thing. The places where this sort of pattern is most useful are where you don't want to handle errors in depends_on_success_of_thing at all, because to have thrown an error is very unexpected behavior. Granted scenarios like that are uncommon, but the most important reason for using the separate `else` scope is to help reduce programmer error because they forgot that the error handling in the catch block will break something when it was entered because depends_on_success_of_thing failed. There's also the matter of readability and cleanliness. It is (in my opinion, at least) an intuitive and concise way to handle a not-uncommon case. |
September 05, 2016 Re: TryElseExpression DIP | ||||
---|---|---|---|---|
| ||||
Posted in reply to pineapple | On 2016-09-05 20:57, pineapple wrote: > In this case, the catch block will catch both errors from do_a_thing and > depends_on_success_of_thing. Then move it to after the "finally" block. -- /Jacob Carlborg |
September 05, 2016 Re: TryElseExpression DIP | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jacob Carlborg | On Monday, 5 September 2016 at 19:12:02 UTC, Jacob Carlborg wrote:
> On 2016-09-05 20:57, pineapple wrote:
>
>> In this case, the catch block will catch both errors from do_a_thing and
>> depends_on_success_of_thing.
>
> Then move it to after the "finally" block.
It would actually have to be inside the "finally" block to reproduce the exact behavior. I included an analog in the original post - I'm not saying that this behavior can't be achieved now, but that this is a concise and more readable pattern which, when utilized, makes it more difficult to accidentally write error-prone code.
Which is easier to read and to write? Which is more maintainable? Which is less prone to programmer errors? This?
bool success = false;
try{
do_a_thing();
success = true;
}catch(Exception exception){
handle_error();
}finally{
try{
if(success){
depends_on_success_of_thing();
}
}finally{
do_this_always();
}
}
Or this?
try{
do_a_thing();
}catch(Exception exception){
handle_error();
}else{
depends_on_success_of_thing();
}finally{
do_this_always();
}
This?
try{
do_a_thing();
}else{
depends_on_success_of_thing();
}
Or this?
bool success = false;
try{
do_a_thing();
success = true;
}finally{
if(success){
depends_on_success_of_thing();
}
}
|
September 05, 2016 Re: TryElseExpression DIP | ||||
---|---|---|---|---|
| ||||
Posted in reply to pineapple | On Monday, 5 September 2016 at 20:04:43 UTC, pineapple wrote: > On Monday, 5 September 2016 at 19:12:02 UTC, Jacob Carlborg wrote: > [...] >> On 2016-09-05 20:57, pineapple wrote: > Which is easier to read and to write? Which is more maintainable? Which is less prone to programmer errors? This? > > [...] > This? > > try{ > do_a_thing(); > }else{ > depends_on_success_of_thing(); > } > > Or this? > > bool success = false; > try{ > do_a_thing(); > success = true; > }finally{ > if(success){ > depends_on_success_of_thing(); > } > } There's probably also a FP approach: function TryElse( alias TryStatements, alias ElseStatements)(){} function TryCatchElseFinally( alias TryStatements, alias ElseStatements, alias CatchStatements, alias ElseStatements, E : Exception = Exception)(){} etc. You pass delegate literals: °°°°°°°°°°°°°°°°°°°°° void TryElse(alias TryStatements, alias ElseStatements)() { bool doElses = true; try TryStatements(); catch(Throwable){} ElseStatements(); } void main() { int i; import std.conv: to; TryElse!( {i = to!int("0.42");}, {i = -1;} ); } °°°°°°°°°°°°°°°°°°°°° With optional sugar to name each statements group: °°°°°°°°°°°°°°°°°°°°° template Statements(alias T) { alias Statements = T; } alias Try = Statements; alias Else = Statements; void main() { TryElse!( Try!({}), Else!({}) ); } °°°°°°°°°°°°°°°°°°°°° |
Copyright © 1999-2021 by the D Language Foundation