September 28, 2016
On 9/28/16 4:18 PM, pineapple wrote:
> On Wednesday, 28 September 2016 at 17:56:13 UTC, Steven Schveighoffer
> wrote:
>> The more I think about this submission, I feel like the benefits are
>> quite slim.
>
> This is not and was not intended to be a glorious, incredible addition
> to the language. It is meant to shove D a couple inches further in the
> direction of modern programming constructs. Everywhere a programmer can
> use `else` instead of mucking about with a boolean success flag

The main question to answer is how big is this "Everywhere". I've never needed it. This obviously isn't proof against, but the situation we are solving here seems very small. Is it worth adding a construct to the language and it seems here we are going to break valid code that already uses else after catch to mean something else?

>
> On Wednesday, 28 September 2016 at 17:56:13 UTC, Steven Schveighoffer
> wrote:
>> For example, should it be valid to use "else" without a catch?
>
> Yes.

Again, try {this} else {that} looks like "try this if it works, else that", which is not what this really does. else only reads right if it follows catch.

>
> On Wednesday, 28 September 2016 at 17:56:13 UTC, Steven Schveighoffer
> wrote:
>> The boolean to indicate an exception was thrown is cumbersome, but not
>> horrible. Having the compiler manage the boolean may make this cleaner
>> (e.g. finally(bool thrown)), I like it better than the else suggestion.
>
> I think this would be an improvement over the current exception
> handling, but `else` is a pre-existing concept and making `finally`
> optionally accept a boolean this way tosses convention on its head, and
> not in a way I think is desireable. If what you're looking for is a
> clean solution, `finally(bool)` is definitely not it.

the pre-existing concept may work for Python, but not be OK for D (i.e. breaks valid code). The boolean concept is how you would implement it by hand, why does this toss it on its head? In fact, any code that requires this kind of flow already uses this mechanism, and can basically be updated to use the new concept by simply removing a few lines and changing finally to finally(bool).

> Moreover, Idan's suggestions about scope sharing make sense to me and I
> don't think his line of thinking would be compatible with doing it the
> way you suggest.

Declaring variables that you need in the right scopes is pretty straightforward. Having scopes magically continue around other separate scopes (catch scopes) doesn't look correct. I get why it's desired, but it doesn't look clean at all.

-Steve
September 28, 2016
On Wednesday, 28 September 2016 at 21:00:00 UTC, Steven Schveighoffer wrote:
> Declaring variables that you need in the right scopes is pretty straightforward. Having scopes magically continue around other separate scopes (catch scopes) doesn't look correct. I get why it's desired, but it doesn't look clean at all.
>
> -Steve

Consider this:

    try {
        auto foo = Foo();
    } catch (FooCreationException) {
        // ...
    } else {
        foo.doSomethingWithFoo();
    }
    // foo does not exist here

versus this:

    Foo foo;
    try {
        foo = Foo();
    } catch (FooCreationException) {
        // ...
    } else {
        foo.doSomethingWithFoo();
    }
    // foo exists here - it could be initialized, it could be not...
September 28, 2016
On 9/28/16 7:17 AM, pineapple wrote:
> These were not documented as a requirement anywhere that I saw. I'd have
> been happy to comply, if I had known this was the practice.

Yah, we're also learning the ropes. Bear with us - this is one of the first DIPs using the new flow.

> I don't know enough about how D is compiled to speak meaningfully about
> implementation details.

I'll formalize this in DIP guide, but some level of implementation discussion is necessary - the more, the better. It has happened in the past (in various languages) that things that seemed sensible were proposed and partially implemented without a clear handle on implementation issues. It would improve the DIP a lot if you collaborated with somebody fluent with implementation issues.

> I am clearly not the only one who's convinced this is a useful feature.
> I welcome you or anyone else who can more effectively express the idea
> to make their own contributions to the DIP.

> If catch and finally don't continue the scope of try, then neither
> should else.

Then I'd say the DIP should discuss the relative advantages and disadvantages compared to the incarnations in other languages. My understanding is it is important for the "else" branch to continue work initiated in the "try" branch. How well does the feature fare without this amenity? It's the kind of question the DIP should address.

> That said, it might be preferable if they all did continue try's scope.
> But this would be a distinct and separate change.

The short answer is that will never happen.

>> * The "Breaking changes" section should include how the following
>> change in semantics would be addressed. Consider:
>>
>>   try
>>     if (expression)
>>         try { ... }
>>         catch (Exception) { ... }
>>     else { ... }
>>   finally { ... }
>>
>> This code is currently legal and binds the "else" clause to the "if"
>> and the "finally" to the first "try". The proposed feature will bind
>> the "else" and the "finally" to the second try and then probably fail
>> to compile because there is no "catch" or "finally" matching the first
>> "try".
>
> This possibility hadn't occurred to me. Is that really legal?
>
> If so, I'd argue the old behavior should be maintained and also that
> people shouldn't write such ambiguous code.

For a DIP to be successful, its authors need to be fluent with syntactical matters and be able to enumerate them and how they are supposed to be handled ("else" also works with "static if", "version", and since DIP 1002 itself, other "try" statements).


Andrei

September 28, 2016
On 9/28/16 7:36 AM, Dicebot wrote:
> Merged draft DIPs are not set in stone - Andrei's comment is not sign of
> immediate rejection but a request to make a PR to improve mentioned things.

It's not a sign of rejection at all! I'm not even discussing the technical merits of the language feature itself. For now I'm looking at ways to make it better. -- Andrei
September 28, 2016
On 9/28/16 9:10 AM, pineapple wrote:
> I submitted a PR addressing some of the mentioned criticisms:
> https://github.com/dlang/DIPs/pull/46

Thanks. The Phobos examples compared:

(1)

// Current
unittest
{
    bool ok = true;
    try
    {
        auto r2 = assumeSorted([ 677, 345, 34, 7, 5 ]);
        debug ok = false;
    }
    catch (Throwable)
    {
    }
    assert(ok);
}
// With try/else
unittest
{
    try
    {
        auto r2 = assumeSorted([ 677, 345, 34, 7, 5 ]);
    }
    catch (Throwable)
    {
    }
    else assert(ok);
}
// Within existing D
unittest
{
    try
    {
        auto r2 = assumeSorted([ 677, 345, 34, 7, 5 ]);
    }
    catch (Throwable)
    {
        return;
    }
    assert(0);
}

(2)

// Current
    bool failed = false;
    try
    {
        auto d = c.get!(int);
    }
    catch (Exception e)
    {
        //writeln(stderr, e.toString);
        failed = true;
    }
    assert(failed); // :o)
// With try/else
    try
    {
        auto d = c.get!(int);
    }
    catch (Exception e)
    {
    }
    else
    {
        assert(0); // :o)
    }
// Within existing D
    assertThrown(c.get!(int));

(An argument here may be made that assertThrown is "cheating".)

(3)

// Current
void assertNot(string s)
{
    bool b = false;
    try { decode(s,DecodeMode.STRICT); }
    catch (DecodeException e) { b = true; }
    assert(b,s);
}

// With try/else
void assertNot(string s)
{
    try { decode(s,DecodeMode.STRICT); }
    catch (DecodeException e) {}
    else { assert(0, s); }
}
// With scope
void assertNot(string s)
{
    try { scope(success) assert(0, s); decode(s,DecodeMode.STRICT); }
    catch (DecodeException e) {}
}

The burden is to make the case that the try/else variant is better than the two others.


Andrei

September 28, 2016
On 9/28/16 7:40 PM, Andrei Alexandrescu wrote:
>
> // With scope
> void assertNot(string s)
> {
>     try { scope(success) assert(0, s); decode(s,DecodeMode.STRICT); }
>     catch (DecodeException e) {}
> }

Sorry, this is not semantically equivalent. Replace with:

// With scope
void assertNot(string s)
{
    try { decode(s,DecodeMode.STRICT); }
    catch (DecodeException e) { return; }
    assert(0, s);
}


Andrei

September 28, 2016
On 9/28/2016 3:12 PM, Idan Arye wrote:
> Consider this:
>
>     try {
>         auto foo = Foo();
>     } catch (FooCreationException) {
>         // ...
>     } else {
>         foo.doSomethingWithFoo();
>     }

I agree with Steven. Having the 'else' continue on with a scope that is already closed by } is very weird and unsettling.

You could argue that D already does this with static if:

    static if (x)
    {
        int x;
    }
    x = 3;

and that disturbs some people. But it is conditional compilation, and no other way around it has been proposed, and it has major benefits. (C++'s static if proposal does not allow this, a decision that I predict they'll come to regret.)

Adding more cases of this sort of thing should be approached with great trepidation, and is not justified if it is only a minor improvement.
September 29, 2016
On Wednesday, 28 September 2016 at 20:18:06 UTC, pineapple wrote:
> On Wednesday, 28 September 2016 at 17:56:13 UTC, Steven Schveighoffer wrote:
>> The more I think about this submission, I feel like the benefits are quite slim.
>
> This is not and was not intended to be a glorious, incredible addition to the language. It is meant to shove D a couple inches further in the direction of modern programming constructs. Everywhere a programmer can use `else` instead of mucking about with a boolean success flag and having to make absolutely sure the code intended to handle a success state doesn't and will never be modified to throw an exception that the error handling code isn't designed for means less time spent on tedium, and less opportunity for programmer error.

Agree, programming should be fun, not struggle.
September 29, 2016
On Wednesday, 28 September 2016 at 23:44:19 UTC, Andrei Alexandrescu wrote:
> On 9/28/16 7:40 PM, Andrei Alexandrescu wrote:
>>
>> // With scope
>> void assertNot(string s)
>> {
>>     try { scope(success) assert(0, s); decode(s,DecodeMode.STRICT); }
>>     catch (DecodeException e) {}
>> }
>
> Sorry, this is not semantically equivalent. Replace with:
>
> // With scope
> void assertNot(string s)
> {
>     try { decode(s,DecodeMode.STRICT); }
>     catch (DecodeException e) { return; }
>     assert(0, s);
> }
>
>
> Andrei

Aren't those the same, as assert won't ever throw a DecodeException?

Simplest implementation:

try {
    decode(s,DecodeMode.STRICT);
    assert(0, s);
} catch (DecodeException e) {}
September 29, 2016
On Thursday, 29 September 2016 at 00:35:47 UTC, Walter Bright wrote:
> I agree with Steven. Having the 'else' continue on with a scope that is already closed by } is very weird and unsettling.

Seconded

> But it is conditional compilation, and no other way around it has been proposed, and it has major benefits. (C++'s static if proposal does not allow this, a decision that I predict they'll come to regret.)

The C++ static_if will create a new scope? Huge mistake IMO. The fact that the static if doesn't create a new scope is a trick that's used a lot in D and Phobos specifically.

Case in point: https://github.com/dlang/phobos/blob/a5e935370f5de590532bb5d12610c865343f56a9/std/string.d#L5859

> Adding more cases of this sort of thing should be approached with great trepidation, and is not justified if it is only a minor improvement.

I agree; static if is different because it's a compile time feature and not a run time feature.