October 03, 2023

On Tuesday, 3 October 2023 at 13:07:00 UTC, Steven Schveighoffer wrote:

>

Now, you can define a further opIndexAssign(T val, size_t idx). However, now you lose capabilities like a[0]++, which I don't think has a possibility of implementing using an opIndex operator, and it would be pretty ugly if you had to.

Works for me, with both ++ and +=: https://run.dlang.io/is/JckTVG

AST output confirms that these are lowered to use opIndex.

Looking at the spec, it seems like opIndex would only be pre-empted here if you overloaded opIndexUnary (for ++) and/or opIndexOpAssign (for +=).

October 03, 2023
On 10/3/23 12:09 PM, Paul Backus wrote:
> On Tuesday, 3 October 2023 at 13:07:00 UTC, Steven Schveighoffer wrote:
>>
>> Now, you can define a further `opIndexAssign(T val, size_t idx)`. However, now you lose capabilities like `a[0]++`, which I don't think has a possibility of implementing using an `opIndex` operator, and it would be pretty ugly if you had to.
> 
> Works for me, with both `++` and `+=`: https://run.dlang.io/is/JckTVG
> 
> AST output confirms that these are lowered to use `opIndex`.
> 
> Looking at the spec, it seems like `opIndex` would only be pre-empted here if you overloaded `opIndexUnary` (for `++`) and/or `opIndexOpAssign` (for `+=`).

OK, so it's not as bad as I thought, but surely the compiler should recognize that `opIndexAssign(val, idx)` doesn't work, but `opIndex(idx) = val` does?

-Steve
October 03, 2023
On Tuesday, 3 October 2023 at 16:45:39 UTC, Steven Schveighoffer wrote:
>
> OK, so it's not as bad as I thought, but surely the compiler should recognize that `opIndexAssign(val, idx)` doesn't work, but `opIndex(idx) = val` does?

Maybe. On the other hand, if you make a typo in the body of your templated `opIndexAssign` overload, do you want the compiler to silently fall back to `opIndex`, or do you want an error? There are pros and cons to both approaches.

At the very least, the spec should do a better job of documenting when the compiler will try a fallback and when it won't.
October 03, 2023
On Tuesday, 3 October 2023 at 17:52:20 UTC, Paul Backus wrote:
> On Tuesday, 3 October 2023 at 16:45:39 UTC, Steven Schveighoffer wrote:
>>
>> OK, so it's not as bad as I thought, but surely the compiler should recognize that `opIndexAssign(val, idx)` doesn't work, but `opIndex(idx) = val` does?
>
> Maybe. On the other hand, if you make a typo in the body of your templated `opIndexAssign` overload, do you want the compiler to silently fall back to `opIndex`, or do you want an error? There are pros and cons to both approaches.
>
> At the very least, the spec should do a better job of documenting when the compiler will try a fallback and when it won't.

Who will be the hero and add the documentation? 😇
October 03, 2023

On Tuesday, 3 October 2023 at 18:09:55 UTC, Imperatorn wrote:

> >

At the very least, the spec should do a better job of documenting when the compiler will try a fallback and when it won't.

Who will be the hero and add the documentation? 😇

More importantly, is there a priority order? Because in our last example, when we leave a single overload, all features are executed through the ref opIndex except the bit:

struct S
{
  int[] i;

  ref opIndex(size_t index) => i[index];

  //void opIndexAssign(int value) { i[] = value; }
  //void opIndexAssign(int value, size_t idx) { i[idx] = value; }
}

void main()
{
  auto s = S([2, 2]);

  s[0] = 2;
  assert(s.i == [2, 2]);

  s[1] = 42;
  assert(s.i == [2, 42]);

  s[0]++;
  assert(s.i == [3, 42]);

  s[0] += 1;
  assert(s.i == [4, 42]);
}

So the important thing is: Who is dominant when another overload comes into play?

SDB@79

October 03, 2023

On Tuesday, 3 October 2023 at 18:29:49 UTC, Salih Dincer wrote:

>

More importantly, is there a priority order? Because in our last example, when we leave a single overload, all features are executed through the ref opIndex except the bit:

The spec says:

>

If an index expression can be rewritten using opIndexAssign or opIndexOpAssign, those are preferred over opIndex.

Source: https://dlang.org/spec/operatoroverloading.html#array

So, the more specialized overloads are tried first, and the more general opIndex is tried last.

The only thing that's unclear here is the meaning of "can be rewritten"--as we've seen, the compiler only checks whether the specialized function exists, not whether it actually works, and will sometimes perform rewrites even when the resulting code does not compile.

October 05, 2023

Well, in the D programming language, both opIndex and opSlice are two different operators used to access elements of a custom type.

struct S(T)
{
    T[] arr;

    T opIndex(size_t index) const
    {
        assert(index < arr.length, "Index out of range");
        return arr[index];
    }

    T[] opSlice(size_t startIndex, size_t endIndex) const
    {
        assert(startIndex <= endIndex && endIndex <= arr.length, "Invalid slice indices");
        return arr[startIndex..endIndex];
    }
}

alias Type = int;
void main()
{
    auto s = S!Type([1, 2, 3]);

    auto element = s[0]; // Calls s.opIndex()
    assert(element == 1);

    auto slice = s[1..3]; // Calls s.opSlice()
    assert(slice == [2, 3]);
}

Thanks

October 05, 2023

On Thursday, 5 October 2023 at 16:40:49 UTC, Gaurav Negi wrote:

>

Well, in the D programming language, both opIndex and opSlice are two different operators used to access elements of a custom type.

Yeah, D is on its way to becoming a near-perfect programming language...

enum initialSituation = [1, 2, 3];
import std.stdio;

void main()
{
  auto s = S(initialSituation);

  s[] = 1;
  s[1] = 2;
  s[2] += 2;
  
  assert(s.arr == initialSituation);

  auto d = s *= 2;
  
  assert(d == S([2, 4, 6]));
}
/* Prints:
[1, 1, 1]: onlineapp.S.opSliceAssign
[1, 2, 1]: onlineapp.S.opIndexAssign
[1, 2, 1]: onlineapp.S.opIndex
[2, 4, 6]: onlineapp.S.opOpAssign!"*".opOpAssign
*/

struct S
{
  int[] arr;

  auto opSliceAssign (int value)
  {
    scope(exit)
    {
      writefln("%s: %s", arr, __FUNCTION__);
    }
    return arr[] = value;
  }

  auto opIndexAssign(int value, int index)
  {
    scope(exit)
    {
      writefln("%s: %s", arr, __FUNCTION__);
    }
    arr[index] = value;
  }

  ref opIndex(size_t index)
  {
    scope(exit)
    {
      writefln("%s: %s", arr, __FUNCTION__);
    }
    return arr[index];
  }

  ref opOpAssign(string op)(int value)
  {
    scope(exit)
    {
      writefln("%s: %s", arr, __FUNCTION__);
    }
    mixin("arr[] " ~ op ~ "=value;");
    return this;
  }
}

SDB@79

1 2
Next ›   Last »