May 15, 2019
On Tuesday, 14 May 2019 at 16:15:20 UTC, H. S. Teoh wrote:
> On Tue, May 14, 2019 at 03:49:51PM +0000, Q. Schroll via Digitalmars-d wrote:
>> I whish, D has these operators:
>> 
>> opStaticIndex
>> opStaticIndexAssign
>> opStaticIndexOpAssign
>> opStaticSlice
>> 
>> When used, they look the same way as opIndex and friends, but the stuff between [ and ] would be bound to template parameters (instead of runtime parameters) and be known at compile time. Basically, this allows to implement static indexing the same way it is possible for an AliasSeq.
>
> +1.  I'd love to have this feature too.
>
>
> T

I've made it into a DIP: https://github.com/dlang/DIPs/pull/155

May 15, 2019
On Wednesday, 15 May 2019 at 01:07:10 UTC, Q. Schroll wrote:
> On Tuesday, 14 May 2019 at 16:15:20 UTC, H. S. Teoh wrote:
>> On Tue, May 14, 2019 at 03:49:51PM +0000, Q. Schroll via Digitalmars-d wrote:
>>> I whish, D has these operators:
>>> 
>>> opStaticIndex
>>> opStaticIndexAssign
>>> opStaticIndexOpAssign
>>> opStaticSlice
>>> 
>>> When used, they look the same way as opIndex and friends, but the stuff between [ and ] would be bound to template parameters (instead of runtime parameters) and be known at compile time. Basically, this allows to implement static indexing the same way it is possible for an AliasSeq.
>>
>> +1.  I'd love to have this feature too.
>>
>>
>> T
>
> I've made it into a DIP: https://github.com/dlang/DIPs/pull/155

Does it need to be called opStaticIndex? Could it just be called opIndex and require the specific template arguments. If you define opIndex(int)() now, it will never be called right (unless you specifically invoke it)?
May 15, 2019
On Wednesday, 15 May 2019 at 01:42:57 UTC, Exil wrote:
> On Wednesday, 15 May 2019 at 01:07:10 UTC, Q. Schroll wrote:
>> On Tuesday, 14 May 2019 at 16:15:20 UTC, H. S. Teoh wrote:
>>> On Tue, May 14, 2019 at 03:49:51PM +0000, Q. Schroll via Digitalmars-d wrote:
>>>> [...]
>>>
>>> +1.  I'd love to have this feature too.
>>>
>>>
>>> T
>>
>> I've made it into a DIP: https://github.com/dlang/DIPs/pull/155
>
> Does it need to be called opStaticIndex? Could it just be called opIndex and require the specific template arguments. If you define opIndex(int)() now, it will never be called right (unless you specifically invoke it)?

I don't have thought about names much. It is not completely obvious to me that reusing existing names works *in any circumstance*.
May 15, 2019
On Wednesday, 15 May 2019 at 01:42:57 UTC, Exil wrote:
> Does it need to be called opStaticIndex? Could it just be called opIndex and require the specific template arguments. If you define opIndex(int)() now, it will never be called right (unless you specifically invoke it)?

Correct. But opIndex(T...)(Foo!T t) may be, and could then be called as either

    s[0](Foo!0.init)

or

    s[Foo!0.init]

Not sure if this is a big issue, but it's at least indicative of possible other issues.

--
  Simen
May 15, 2019
On Wednesday, 15 May 2019 at 01:42:57 UTC, Exil wrote:
> On Wednesday, 15 May 2019 at 01:07:10 UTC, Q. Schroll wrote:
>
> Does it need to be called opStaticIndex? Could it just be called opIndex and require the specific template arguments. If you define opIndex(int)() now, it will never be called right (unless you specifically invoke it)?

I've thought about it and concluded the answer is no.

For dynamic indexing (i.e. current state opIndex and friends) most of them (everything except opDollar) must be some kind of function (i.e. functions, function templates, something with opCall, ... you name it), as it is rewritten to a function call.
With opStaticIndex, apart from opStaticIndex(Op)Assign, where the right-hand side is still a function parameter, it could be implemented by an enum or alias or something like that (cf. current state opDollar).
You cannot overload functions with enums or something the like.
Then you need two different names.
You could fallback to that if the opStatic.. ones are not there, but that complicates the DIP even more.

I'd stay with the proposed names or change them to something different, but not reuse something that currently exists.
May 15, 2019
On Tue, May 14, 2019 at 9:15 AM H. S. Teoh via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
>
> On Tue, May 14, 2019 at 03:49:51PM +0000, Q. Schroll via Digitalmars-d wrote:
> > I whish, D has these operators:
> >
> > opStaticIndex
> > opStaticIndexAssign
> > opStaticIndexOpAssign
> > opStaticSlice
> >
> > When used, they look the same way as opIndex and friends, but the stuff between [ and ] would be bound to template parameters (instead of runtime parameters) and be known at compile time. Basically, this allows to implement static indexing the same way it is possible for an AliasSeq.
>
> +1.  I'd love to have this feature too.

I have wanted this so many times. In C++, we can check if a value is a literal with some stupid __builtin_whatever() thing, and that can drive patterns like this, but that's terrible, and this proposal is obviously the right way to go about it.
May 15, 2019
On Wednesday, 15 May 2019 at 09:05:20 UTC, Simen Kjærås wrote:
> On Wednesday, 15 May 2019 at 01:42:57 UTC, Exil wrote:
>> Does it need to be called opStaticIndex? Could it just be called opIndex and require the specific template arguments. If you define opIndex(int)() now, it will never be called right (unless you specifically invoke it)?
>
> Correct. But opIndex(T...)(Foo!T t) may be, and could then be called as either
>
>     s[0](Foo!0.init)
>
> or
>
>     s[Foo!0.init]
>
> Not sure if this is a big issue, but it's at least indicative of possible other issues.
>
> --
>   Simen


    s[0](Foo!0.init) // -> s.opIndex!(0)()(Foo!0.init)
    s[Foo!0.init]    // -> s.opIndex(Foo!0.init);

If it compiled would entire depend on what opIndex returns, the arguments passed to opIndex() are the values in "[]" not the brackets, which is a separate call.

Unless you meant this?

    s[0, Foo!0.init];

Which would then be difficult to know which function to call as it takes in a compile-time value and a run-time value.



May 16, 2019
On Wednesday, 15 May 2019 at 20:04:29 UTC, Exil wrote:
> On Wednesday, 15 May 2019 at 09:05:20 UTC, Simen Kjærås wrote:
>> On Wednesday, 15 May 2019 at 01:42:57 UTC, Exil wrote:
>>> Does it need to be called opStaticIndex? Could it just be called opIndex and require the specific template arguments. If you define opIndex(int)() now, it will never be called right (unless you specifically invoke it)?
>>
>> Correct. But opIndex(T...)(Foo!T t) may be, and could then be called as either
>>
>>     s[0](Foo!0.init)
>>
>> or
>>
>>     s[Foo!0.init]
>>
>> Not sure if this is a big issue, but it's at least indicative of possible other issues.
>>
>> --
>>   Simen
>
>
>     s[0](Foo!0.init) // -> s.opIndex!(0)()(Foo!0.init)
>     s[Foo!0.init]    // -> s.opIndex(Foo!0.init);
>
> If it compiled would entire depend on what opIndex returns, the arguments passed to opIndex() are the values in "[]" not the brackets, which is a separate call.
>
> Unless you meant this?
>
>     s[0, Foo!0.init];
>
> Which would then be difficult to know which function to call as it takes in a compile-time value and a run-time value.

I meant exactly what I wrote. Since s[0] would be compile-time, the arguments passed to opIndex would indeed be 0. This would evaluate to a function taking a Foo!0. We could define that to be immediately called with no run-time arguments, but that complicates certain patterns that may be of interest:

struct S {
    template opIndex(size_t idx) {
        static if (idx == 0) alias opIndex = fun1;
        else alias opIndex = fun2;
    }

    void fun1(size_t i)(Foo!i value) {}
    void fun2() {}
}

S s;
s[0](Foo!0.init); // Should call fun1 with Foo!0.init.
s[1](); // Should call fun2 with no args.

If immediate call was implemented, the above would look like this:

struct S {
    auto opIndex(size_t idx)() {
        static if (idx == 0) return &fun1!idx;
        else return &fun2;
    }

    void fun1(size_t i)(Foo!i value) {}
    void fun2() {}
}

Suddenly I'm dealing with function pointers instead of aliases, and this forces you to special-case functions while values, types, and aliases to everything but functions follow the same pattern.

Of course, if no run-time arguments were given and there is no & operator before s[0], calling the returned function immediately might be the sane thing to do.

--
  Simen
May 16, 2019
On Thursday, 16 May 2019 at 08:28:35 UTC, Simen Kjærås wrote:
> On Wednesday, 15 May 2019 at 20:04:29 UTC, Exil wrote:
>> On Wednesday, 15 May 2019 at 09:05:20 UTC, Simen Kjærås wrote:
>>> On Wednesday, 15 May 2019 at 01:42:57 UTC, Exil wrote:
>>>> Does it need to be called opStaticIndex? Could it just be called opIndex and require the specific template arguments. If you define opIndex(int)() now, it will never be called right (unless you specifically invoke it)?
>>>
>>> Correct. But opIndex(T...)(Foo!T t) may be, and could then be called as either
>>>
>>>     s[0](Foo!0.init)
>>>
>>> or
>>>
>>>     s[Foo!0.init]
>>>
>>> Not sure if this is a big issue, but it's at least indicative of possible other issues.
>>>
>>> --
>>>   Simen
>>
>>
>>     s[0](Foo!0.init) // -> s.opIndex!(0)()(Foo!0.init)
>>     s[Foo!0.init]    // -> s.opIndex(Foo!0.init);
>>
>> If it compiled would entire depend on what opIndex returns, the arguments passed to opIndex() are the values in "[]" not the brackets, which is a separate call.
>>
>> Unless you meant this?
>>
>>     s[0, Foo!0.init];
>>
>> Which would then be difficult to know which function to call as it takes in a compile-time value and a run-time value.
>
> I meant exactly what I wrote. Since s[0] would be compile-time, the arguments passed to opIndex would indeed be 0. This would evaluate to a function taking a Foo!0. We could define that to be immediately called with no run-time arguments, but that complicates certain patterns that may be of interest:
>
> struct S {
>     template opIndex(size_t idx) {
>         static if (idx == 0) alias opIndex = fun1;
>         else alias opIndex = fun2;
>     }
>
>     void fun1(size_t i)(Foo!i value) {}
>     void fun2() {}
> }
>
> S s;
> s[0](Foo!0.init); // Should call fun1 with Foo!0.init.
> s[1](); // Should call fun2 with no args.
>
> If immediate call was implemented, the above would look like this:
>
> struct S {
>     auto opIndex(size_t idx)() {
>         static if (idx == 0) return &fun1!idx;
>         else return &fun2;
>     }
>
>     void fun1(size_t i)(Foo!i value) {}
>     void fun2() {}
> }
>
> Suddenly I'm dealing with function pointers instead of aliases, and this forces you to special-case functions while values, types, and aliases to everything but functions follow the same pattern.
>
> Of course, if no run-time arguments were given and there is no & operator before s[0], calling the returned function immediately might be the sane thing to do.
>
> --
>   Simen

Well now your example doesn't have opIndex(T...)() that your original post did. I also don't see how this is a problem specifically if it was named opIndex. Seems more like a detail that would have to be ironed out either way to ensure templates can be used with opStaticIndex/opIndex.
May 16, 2019
On Thursday, 16 May 2019 at 21:13:24 UTC, Exil wrote:
> On Thursday, 16 May 2019 at 08:28:35 UTC, Simen Kjærås wrote:
>> struct S {
>>     template opIndex(size_t idx) {
>>         static if (idx == 0) alias opIndex = fun1;
>>         else alias opIndex = fun2;
>>     }
>>
>>     void fun1(size_t i)(Foo!i value) {}
>>     void fun2() {}
>> }
>
> Well now your example doesn't have opIndex(T...)() that your original post did.

Sure it does. There's essentially no difference between fun1 and fun2 here:

template fun1(T...) {
    alias fun1 = impl;
}

int impl() {
    return 3;
}

int fun2(T...)() {
    return 3;
}

unittest {
    auto a = fun1!(1,2,3);
    auto b = fun2!(1,2,3);
}

In the same way, whether I write auto opIndex()() {} or alias opIndex = ... is irrelevant. The behavior should be exactly the same.


> I also don't see how this is a problem specifically if it was named opIndex. Seems more like a detail that would have to be ironed out either way to ensure templates can be used with opStaticIndex/opIndex.

It's a problem because opIndex comes with special syntax that other symbols don't have.

--
  Simen