Thread overview
possible improvement to if statements?
Apr 26, 2020
Sebastiaan Koppe
Apr 27, 2020
Sebastiaan Koppe
Apr 27, 2020
Sebastiaan Koppe
Apr 26, 2020
Meta
April 26, 2020
We have this pattern in D, which is pretty nice:

if(auto p = x in y)
{
   // use p which is a pointer
}

But pointers can be difficult to use -- you sometimes have to dereference them to use them, and you can't create a reference variable in-line.

What about something like:

if(ref p = ptrExpression)
{
}

The expression would be a pointer expression. p would be bound to the pointer target, but the if statement is on the pointer itself. That is, you get inside only if p is referring to something not null.

I realize this is not going to fly, because there are too many holes. But I've wished for something like this a few times.

You can ALMOST get there with a kooky foreach helper (foreach is one of the few places where a ref local can be added into a function scope):

struct RefOf(T)
{
    T* ptr;
    ref T front() { return *ptr; }
    bool empty() { return ptr is null; }
    void popFront() { ptr = null; }
}

RefOf!T refof(T)(T * ptr) { return RefOf!T(ptr); }

foreach(ref r; (x in y).refof)
{
}

but you can't do `else` with it. You can wrap it in an if like this:

if(auto p = x in y) foreach(ref r; p.refof) {
   // ugh...
}
else
{
}

Does anyone have a workable way this kind of thing could be added to D?

-Steve
April 26, 2020
On Sunday, 26 April 2020 at 20:41:47 UTC, Steven Schveighoffer wrote:
> Does anyone have a workable way this kind of thing could be added to D?
>
> -Steve

Did you consider this?

```
import std.stdio;

struct Wrap(P) {
    P* p;
    this(P* p) {
        this.p = p;
    }
    bool opCast(T)() if (is(T == bool)) {
        return p !is null;
    }
    ref P get() { return *p; }
    alias get this;
}

auto wrap(P)(P* p) {
    return Wrap!P(p);
}

void main() {
    struct Foo {
        int bar;
    }
    Foo[string] x = ["a": Foo(5)];
    if (auto r = wrap("a" in x)) {
        r.bar = 7;
    }
    writeln(x);          // prints ["a":Foo(7)]
}
```
April 26, 2020
On 4/26/20 5:06 PM, Sebastiaan Koppe wrote:
> On Sunday, 26 April 2020 at 20:41:47 UTC, Steven Schveighoffer wrote:
>> Does anyone have a workable way this kind of thing could be added to D?
>>
> 
> Did you consider this?

No I haven't, that's at least as good as the foreach version (though having a simple ref is nicer than having a wrapper type). Is this located anywhere on code.dlang.org or in phobos?

But there is one issue -- what if P defines opCast(bool)?

Still it's almost good enough, and probably better than what I wrote.

I would still like an improvement to this pattern that fits what I want to do -- essentially, if this exists, give me a reference to it, and let me use it.

-Steve
April 26, 2020
On Sunday, 26 April 2020 at 20:41:47 UTC, Steven Schveighoffer wrote:
> We have this pattern in D, which is pretty nice:
>
> if(auto p = x in y)
> {
>    // use p which is a pointer
> }
>
> But pointers can be difficult to use -- you sometimes have to dereference them to use them, and you can't create a reference variable in-line.
>
> What about something like:
>
> if(ref p = ptrExpression)
> {
> }
>
> The expression would be a pointer expression. p would be bound to the pointer target, but the if statement is on the pointer itself. That is, you get inside only if p is referring to something not null.
>
> I realize this is not going to fly, because there are too many holes. But I've wished for something like this a few times.
>
> You can ALMOST get there with a kooky foreach helper (foreach is one of the few places where a ref local can be added into a function scope):
>
> struct RefOf(T)
> {
>     T* ptr;
>     ref T front() { return *ptr; }
>     bool empty() { return ptr is null; }
>     void popFront() { ptr = null; }
> }
>
> RefOf!T refof(T)(T * ptr) { return RefOf!T(ptr); }
>
> foreach(ref r; (x in y).refof)
> {
> }
>
> but you can't do `else` with it. You can wrap it in an if like this:
>
> if(auto p = x in y) foreach(ref r; p.refof) {
>    // ugh...
> }
> else
> {
> }
>
> Does anyone have a workable way this kind of thing could be added to D?
>
> -Steve

The real answer is flow-based typing (which is a nuke for this particular cockroach of a problem, but has many more benefits besides).
April 27, 2020
On Sunday, 26 April 2020 at 21:26:31 UTC, Steven Schveighoffer wrote:
> Is this located anywhere on code.dlang.org or in phobos?

No, hence the awful name.

> But there is one issue -- what if P defines opCast(bool)?

I think you can introspect that in the opCast function, and if so, forward the call if p !is null.

That still leaves you with the downside of wrapping the (x in y) expression.

You could also argue that the (x in y) expression needs to return an optional. If ever there was a need for optional this would be it. If fact the Wrap thing I wrote is dangerously close to the nullable, including it's flaws, namely that it automatically dereferences p.

A more function approach might be better. Gets rid of the control flow as well.

y.get(x)
  .then(x => writeln(x));   // prints only when x is in y

y.get(x)
  .withDefault(55)
  .then(x => writeln(x));   // prints x or 55

y.get(x)
  .then(x => writeln(x))
  .orElse({ throw new Exception("not found")});   // prints x or throws
April 27, 2020
On Sunday, 26 April 2020 at 21:26:31 UTC, Steven Schveighoffer wrote:
> Is this located anywhere on code.dlang.org or in phobos?

No, hence the awful name.

> But there is one issue -- what if P defines opCast(bool)?

I think you can introspect that in the opCast function, and if so, forward the call if p !is null.

That still leaves you with the downside of wrapping the (x in y) expression.

You could also argue that the (x in y) expression needs to return an optional. If ever there was a need for optional this would be it. If fact the Wrap thing I wrote is dangerously close to the nullable, including it's flaws, namely that it automatically dereferences p.

A more function approach might be better. Gets rid of the control flow as well.

y.get(x)
  .then(x => writeln(x));   // prints only when x is in y

y.get(x)
  .withDefault(55)
  .then(x => writeln(x));   // prints x or 55

y.get(x)
  .then(x => writeln(x))
  .orElse({ throw new Exception("not found")});   // prints x or throws