May 16, 2019
On Thursday, 16 May 2019 at 21:36:43 UTC, Simen Kjærås wrote:
> 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

This doesn't help with the explanation, what special syntax? Maybe an example that works with opStaticIndex and that wouldn't work with opIndex would help. Otherwise I still don't see a problem.
May 17, 2019
On Thursday, 16 May 2019 at 22:26:34 UTC, Exil wrote:
> This doesn't help with the explanation, what special syntax? Maybe an example that works with opStaticIndex and that wouldn't work with opIndex would help. Otherwise I still don't see a problem.

Simple answer is: You cannot overload opIndex aliasing a data member and opIndex being a function. If it is a function somewhere, it *must* always be a function in that aggregate (struct/union/class/interface).

It is more flexible not having a name clash. It's easier to learn and reason about, too. To me, it has literally no benefit using opIndex for static indexing.

Part of a working example: https://run.dlang.io/is/274pNN
May 17, 2019
On Friday, 17 May 2019 at 17:41:26 UTC, Q. Schroll wrote:
> On Thursday, 16 May 2019 at 22:26:34 UTC, Exil wrote:
>> This doesn't help with the explanation, what special syntax? Maybe an example that works with opStaticIndex and that wouldn't work with opIndex would help. Otherwise I still don't see a problem.
>
> Simple answer is: You cannot overload opIndex aliasing a data member and opIndex being a function. If it is a function somewhere, it *must* always be a function in that aggregate (struct/union/class/interface).

That's what template specialization is exactly for. Exactly how it can be used:

struct A {

    template opIndex(int i) {
        static if(i == 0) alias opIndex = member1;
        else alias opIndex = member2;
    }

    int member1;
    double member2;


    int opIndex( int a, int b ) { return 0; }

    int opIndex(T...)( T a ) { return 1; }

}

void main() {
	A a;

    a[0,0];
    a[0];

    a.opIndex!0 = 10;   // a[0] = 10
    a.opIndex!1 = 10.1; // a[1] = 10.1

    writeln( a.member1, a.member2 );
}

> It is more flexible not having a name clash. It's easier to learn and reason about, too. To me, it has literally no benefit using opIndex for static indexing.
>
> Part of a working example: https://run.dlang.io/is/274pNN

So what if you have 2 parameters, one is a runtime value and the other is a compile time value? Would it be a compile error then? But then you can implement opIndex and opStaticIndex, so you can use two runtime values and two compile time values but if for some reason you need one, instead you will need some hacky work around to get it to work:

    arr[ 0, someRunTimeIndex() ] = 10; // error mixing compile-time and run-time

    int i = 0;
    arr[ i, someRunTimeIndex() ] = 10; // ok
May 18, 2019
On Friday, 17 May 2019 at 19:52:53 UTC, Exil wrote:
> On Friday, 17 May 2019 at 17:41:26 UTC, Q. Schroll wrote:
>> On Thursday, 16 May 2019 at 22:26:34 UTC, Exil wrote:
>>> This doesn't help with the explanation, what special syntax? Maybe an example that works with opStaticIndex and that wouldn't work with opIndex would help. Otherwise I still don't see a problem.
>>
>> Simple answer is: You cannot overload opIndex aliasing a data member and opIndex being a function. If it is a function somewhere, it *must* always be a function in that aggregate (struct/union/class/interface).
>
> That's what template specialization is exactly for. Exactly how it can be used:

Do you really mean specialization? I'm asking as you don't use it in your following example. Maybe you mean instantiation? I just want to make sure we're talking about the same thing.

> struct A {
>
>     template opIndex(int i) {
>         static if (i == 0) alias opIndex = member1;
>         else alias opIndex = member2;
>     }
>
>     int member1;
>     double member2;
>
>
>     int opIndex(int a, int b) { return 0; }
>
>     int opIndex(T...)(T a) { return 1; }
>
> }
>
> void main() {
> 	A a;
>
>     a[0,0];
>     a[0];
>
>     a.opIndex!0 = 10;   // a[0] = 10
>     a.opIndex!1 = 10.1; // a[1] = 10.1
>
>     writeln(a.member1, a.member2);
> }

I'm sorry, but I don't get what you want to show me. I strongly believe that you have a point somewhere and I'm just not seeing it. I've just tried out that code piece and it worked to my surprise.

I honestly think now, it's actually possible. The question remaining is: Is it really better or rather too complicated?

In the current form of the DIP, if you'd use a type as index, it'd compile. I'll change that, because that's not how it's meant to be.

I find it really difficult to say if widening the abilities of opIndex to opStaticIndex can be done. There are so many corner cases. That's mainly why I proposed a new compiler-recognized name: It's the safe option.

In the current state of D, opIndex must be callable to trigger the rewrite; being present as member is insufficient. As a simple example, if you use std.typecons.Tuple with a member namend "opIndex" of type int, you still can do everything you'd expect from a tuple with reasonable field names. Notably, it doesn't invalidate static indexing. But make "opIndex" of type `int delegate(/*whatever you like*/)`, and the Tuple is unusable: https://run.dlang.io/is/BuiT2a
It's somewhat artificial and not a reason for the DIP. Sure, naming a member "opStaticIndex" would break the tuple, too.

>> It is more flexible not having a name clash. It's easier to learn and reason about, too. To me, it has literally no benefit using opIndex for static indexing.
>>
>> Part of a working example: https://run.dlang.io/is/274pNN
>
> So what if you have 2 parameters, one is a runtime value and the other is a compile time value? Would it be a compile error then? But then you can implement opIndex and opStaticIndex, so you can use two runtime values and two compile time values but if for some reason you need one, instead you will need some hacky work around to get it to work:
>
>     arr[ 0, someRunTimeIndex() ] = 10; // error mixing compile-time and run-time
>
>     int i = 0;
>     arr[ i, someRunTimeIndex() ] = 10; // ok

To use a template, you need *all* arguments at compile-time. Otherwise it's lowered to a run-time indexing operation no matter how many arguments are present at compile-time.

The first call (// error mixing) is not a problem. The fact that some values could be used as a template parameter is irrelevant. The question is: Does the template instantiation work? And the answer is no. That's where double indexing, e.g. tup[0][1] versus tup[0,1] makes a real difference. If you really need mixing sorts of arguments, that's the way I'd do it.

Mixing is not an error. Mixing is just run-time.
1 2 3
Next ›   Last »