Thread overview
improving scope(finally/success)
Dec 03, 2015
Tomer Filiba
Dec 03, 2015
Idan Arye
Dec 03, 2015
Marc Schütz
Dec 03, 2015
Chris Wright
Dec 04, 2015
Artur Skawina
December 03, 2015
it'd be really helpful if scope() statements got hold of the return value or exception, e.g.,

scope(success, retval) {
    writeln("the retval is", retval)
}

scope(failure, ex) {
    if(typeid(ex) == typeid(MyException)) {
        callTheCops();
    }
}

it would make logging very easy, and since these statements are basically code-rewrites i don't suppose it would be hard to implement. from a syntax point of view:

scope(succes[, VARNAME])    // where VARNAME would be the return value (a const tmp variable?)
scope(failure[, VARNAME])   // where VARNAME would be hold the exception (a Throwable)

i mean, code such as

int f() {
    scope(exit) writeln("bye");
    return 5;
}

is rewritten as something like

int f() {
    try {
        auto tmp = 5;
    }
    finally {
        writeln("bye");
    }
    return tmp;
}

so `tmp` is already there for the finally clause (modulo scoping issues)

December 03, 2015
On Thursday, 3 December 2015 at 11:41:29 UTC, Tomer Filiba wrote:
> it'd be really helpful if scope() statements got hold of the return value or exception, e.g.,
>
> scope(success, retval) {
>     writeln("the retval is", retval)
> }
>
> scope(failure, ex) {
>     if(typeid(ex) == typeid(MyException)) {
>         callTheCops();
>     }
> }
>
> it would make logging very easy, and since these statements are basically code-rewrites i don't suppose it would be hard to implement. from a syntax point of view:
>
> scope(succes[, VARNAME])    // where VARNAME would be the return value (a const tmp variable?)
> scope(failure[, VARNAME])   // where VARNAME would be hold the exception (a Throwable)
>
> i mean, code such as
>
> int f() {
>     scope(exit) writeln("bye");
>     return 5;
> }
>
> is rewritten as something like
>
> int f() {
>     try {
>         auto tmp = 5;
>     }
>     finally {
>         writeln("bye");
>     }
>     return tmp;
> }
>
> so `tmp` is already there for the finally clause (modulo scoping issues)

int foo(bool cond) { // scope 1
   { // scope 2
       scope(success, retval) {
           writeln("the retval is", retval)
       }
       if (cond) { // scope 3
           return 1;
       }
   }
   return 2;
}


`foo(true)` should obviously print "the retval is 1", but what should `foo(false)` print? It's `scope(success)` block should run when it exists scope 2, but the return value is only determined at the end of scope 1.
December 03, 2015
On Thursday, 3 December 2015 at 13:07:53 UTC, Idan Arye wrote:
> On Thursday, 3 December 2015 at 11:41:29 UTC, Tomer Filiba wrote:
>> int f() {
>>     scope(exit) writeln("bye");
>>     return 5;
>> }
>>
>> is rewritten as something like
>>
>> int f() {
>>     try {
>>         auto tmp = 5;
>>     }
>>     finally {
>>         writeln("bye");
>>     }
>>     return tmp;
>> }
>>
>> so `tmp` is already there for the finally clause (modulo scoping issues)
>
> int foo(bool cond) { // scope 1
>    { // scope 2
>        scope(success, retval) {
>            writeln("the retval is", retval)
>        }
>        if (cond) { // scope 3
>            return 1;
>        }
>    }
>    return 2;
> }
>
>
> `foo(true)` should obviously print "the retval is 1", but what should `foo(false)` print? It's `scope(success)` block should run when it exists scope 2, but the return value is only determined at the end of scope 1.

Tomer's suggested lowering can't work, because tmp's scope doesn't extend outside of the try. It would need to be hoisted up to the beginning of the function.

This would then imply that `foo(false)` would print "0" (int.init). However, I don't think this kind of lowering is a good idea, because it would create a temporary lvalue, which implies that it needs to go through the full construct/assign/destroy cycle instead of a simple move, which is a potentially observable difference to the current behaviour.
December 03, 2015
You can already do it with a slight change, and it's not so painful, at least in simple functions:

int foo(bool b) {
  auto result = 2;
  scope(exit) writeln(result);
  if (b) {
    result = 1;
  }
  return result;
}
December 03, 2015
On 12/3/15 10:24 AM, Chris Wright wrote:
> You can already do it with a slight change, and it's not so painful, at
> least in simple functions:
>
> int foo(bool b) {
>    auto result = 2;
>    scope(exit) writeln(result);
>    if (b) {
>      result = 1;
>    }
>    return result;
> }
>

auto ref logCall(alias f, Args...)(auto ref Args args) {
   auto ref printResult(T)(auto ref T t)
   {
      writeln(t);
      return t;
   }
   return printResult(f(args))
}

logCall!foo(true);

-Steve
December 04, 2015
On 12/03/15 16:33, Steven Schveighoffer via Digitalmars-d wrote:
> auto ref logCall(alias f, Args...)(auto ref Args args) {
>    auto ref printResult(T)(auto ref T t)
>    {
>       writeln(t);
>       return t;
>    }
>    return printResult(f(args))
> }
> 
> logCall!foo(true);

Which will happily accept:

   bool foo(ref bool a) { return a=false; }


And, yes, it can be dealt with via a bit of introspection, but nobody will bother in practice (consider that 'f' may be overloaded). So it doesn't really work for non-trivial or generic code.

artur