September 29, 2016
`inout` is a useful feature, but it's nearly impossible to actually use it. Suppose I have

struct MyTable {
    @property items() inout {
        return Range(&this);
    }

    static struct Range {
        MyTable* table; // can't be inout
    }
}

I want my items-range to be const if `this` is const or modifiable if `this` is modifiable. I ended up with

    @property items() {
        return Range!false(&this);
    }
    @property items() const {
        return Range!true(&this);
    }

    static struct Range(bool isConst) {
        static if (isConst) {
            const(MyTable)* table;
        } else {
            MyTable* table;
        }
    }

Also, I can't define the struct inside the `items` method since it's used in two other methods (`keys` and `values`), only they hold a different `front`

It would be really nice if I could "capture" the qualifiers and be parameterized on them as well. For example,

int opApply(qual Q)(scope int delegate(int x) Q dg) Q;

Meaning, my opApply is Q if dg is Q. In other words, I'm `nothrow` if dg is `nothrow`, I'm `@nogc` if dg is `@nogc`, I'm `pure` if dg is `pure`, etc. Inout is just a special case of this feature, e.g.

int opApply(qual Q)(scope int delegate(Q int x) dg) Q;

E.g., I'm const if dg takes a const first parameter. So using my first example,

    @property items(qual Q)() Q {
        return Range!Q(&this);
    }

    static struct Range(qual Q) {
        Q(MyTable)* table;
    }

Much more elegant and concise. It needn't generate different object code for instantiations of course, it only needs to be respected by the frontend and thrown away later.


-tomer
September 29, 2016
On 9/29/16 8:53 AM, Tomer Filiba wrote:
> `inout` is a useful feature, but it's nearly impossible to actually use
> it. Suppose I have
>
> struct MyTable {
>     @property items() inout {
>         return Range(&this);
>     }
>
>     static struct Range {
>         MyTable* table; // can't be inout
>     }
> }
>
> I want my items-range to be const if `this` is const or modifiable if
> `this` is modifiable.

Yes, this is a problem -- but not with inout. This is a problem with the lack of tail modifier syntax.

Observe how this works if Range is really a dynamic array:

@property inout(T)[] items() inout {
    return theItems; // a dynamic array
}

Works just fine. What we need is a way to say tailinout(Range), which means that the table member is mutable, but points at inout data.

> I ended up with
>
>     @property items() {
>         return Range!false(&this);
>     }
>     @property items() const {
>         return Range!true(&this);
>     }

There is a way to make this more DRY, but it is still ugly, and loses the benefits of inout. You need to use template this parameters:

struct Range(T)
{
   T *table;
}

@property items(this T)() {
    return Range!T(&this);
}

> It would be really nice if I could "capture" the qualifiers and be
> parameterized on them as well. For example,
>
> int opApply(qual Q)(scope int delegate(int x) Q dg) Q;

I once thought this would work well, but in practice I think it would be a disaster. Imagine everyone defining their own set of qualifiers and how they will work.

It's also a template, and does not protect against modification of mutable data, or static if to change layout/implementation/etc.

I am working on an article to propose a tail modifier syntax. This would solve most of these problems.

-Steve