www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - TryElseExpression DIP

reply pineapple <meapineapple gmail.com> writes:
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();
         }
     }
Sep 05 2016
next sibling parent reply ag0aep6g <anonymous example.com> writes:
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 { ... } ?
Sep 05 2016
parent reply pineapple <meapineapple gmail.com> writes:
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.
Sep 05 2016
parent reply Jacob Carlborg <doob me.com> writes:
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
Sep 05 2016
parent reply pineapple <meapineapple gmail.com> writes:
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(); } }
Sep 05 2016
parent Basile B. <b2.temp gmx.com> writes:
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!({}) ); } °°°°°°°°°°°°°°°°°°°°°
Sep 05 2016
prev sibling parent arturg <var.spool.mail700 gmail.com> writes:
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) { ... }
Sep 05 2016