Jump to page: 1 2
Thread overview
The Phobos Put
Mar 29, 2023
Salih Dincer
Mar 29, 2023
Dennis
Mar 29, 2023
Ali Çehreli
Mar 29, 2023
Salih Dincer
Mar 29, 2023
Ali Çehreli
Mar 29, 2023
ag0aep6g
Mar 29, 2023
Ali Çehreli
Mar 29, 2023
ag0aep6g
Mar 29, 2023
Salih Dincer
Mar 30, 2023
Salih Dincer
Mar 30, 2023
Salih Dincer
Mar 30, 2023
Paul Backus
Mar 31, 2023
Paul Backus
Mar 29, 2023
Salih Dincer
March 29, 2023

Why does my put work but the Phobos put doesn't work with a slice?

>

onlineapp.d(11): Error: none of the overloads of template std.range.primitives.put are callable using argument types !()(int[], int[])
/dlang/dmd/linux/bin64/../../src/phobos/std/range/primitives.d(386): Candidate is: put(R, E)(ref R r, E e)

void main()
{
    import std.range : phobos_put = put;
  
    enum testLen = 4;
    auto a = new int[testLen];
    auto b = new int[testLen];

    auto slice = a[1..$-1];
    slice.phobos_put([2, 3]);
    //a[1..$-1].phobos_put([2, 3]);
    b[1..$-1].put([2, 3]);

    import std.conv : text;
    assert(a == b, text(a));
}

void put(R)(R[] range, R[] source)
{
  assert(source.length <= range.length);
  foreach(element; source)
  {
    range[0] = element;  // range.front()
    range = range[1..$]; // range popFront()
  }
}

SDB@79

March 29, 2023

On Wednesday, 29 March 2023 at 11:10:42 UTC, Salih Dincer wrote:

>

Why does my put work but the Phobos put doesn't work with a slice?

Your put doesn't take range by ref, so it allows you to pass an rvalue. Consequently, it doesn't advance the range from the callers perspective.

March 29, 2023
On 3/29/23 04:48, Dennis wrote:
> On Wednesday, 29 March 2023 at 11:10:42 UTC, Salih Dincer wrote:
>> Why does my `put` work but the Phobos `put` doesn't work with a slice?
>
> Your `put` doesn't take `range` by `ref`, so it allows you to pass an
> rvalue. Consequently, it doesn't advance the range from the callers
> perspective.

And that 'ref' is necessary because not every OutputRange can be sliced for further calls to put(). Salih does not have that problem because he is working with slices, which are (usually) trivially slicable for the next portion to be passed to put().

On the other hand, Phobos's put() works with any OutputRange so it has to take a 'ref' to advance to know where it is left off. This behavior makes its use with slices weird but sometimes such is life. :)

Ali

March 29, 2023

On Wednesday, 29 March 2023 at 15:01:27 UTC, Ali Çehreli wrote:

>

On 3/29/23 04:48, Dennis wrote:
On the other hand, Phobos's put() works with any OutputRange so it has to take a 'ref' to advance to know where it is left off. This behavior makes its use with slices weird but sometimes such is life. :)

Would not adding a prototype without a ref cause ambiguity? In this way, it could also be used directly with slices. For example:

auto put(R)(R[] range, R[] source)
  => putImpl(range, source);

auto put(R)(ref R[] range, R[] source)
  => putImpl(range, source);

void putImpl(R)(ref R[] range, R[] source)
{
  assert(source.length <= range.length);
  foreach(element; source)
  {
    range[0] = element;  // range.front()
    range = range[1..$]; // range.popFront()
  }
}

void main() {
  enum data = [1, 0, 0, 4];
  auto arr = data;
  auto slice = arr[1..$-1];

  slice.put([2]);
  assert(arr == [1, 2, 0, 4]);

  slice.put([3]);
  assert(arr == [1, 2, 3, 4]);

  arr[1..$-1].put([0, 0]);
  assert(arr == data);
}

SDB@79

March 29, 2023
On 3/29/23 09:27, Salih Dincer wrote:

> In this way,
> it could also be used directly with slices. For example:

> auto put(R)(R[] range, R[] source)
>    => putImpl(range, source);

That's for rvalues.

> auto put(R)(ref R[] range, R[] source)
>    => putImpl(range, source);

That's for lvalues.

If you are proposing keeping the current ref-taking Phobos put() as well, then the following call would be ambiguous:

  slice.put([2]);  // Compilation ERROR

That can be remedied by using template constraints for 'slices' vs. other output ranges. But that would be against the idea of "D slices are most capable ranges". What I mean is, I should be able to write any range algorithm and pass a slice to it and it should work. If we go with your proposal, then we would have to check for that case for some algorithms like put().

Further, I think the user of put() in templates would have to check whether they are dealing with a slice or not:

void foo(R)(R range) {
  range.put(/* ... */);

  // If we go with your proposal, whether 'range' changed depends
  // on whether R is a slice or not. Do we have to check with
  // 'static if' in such range algorithms?
}

Note that I am not defending the behavior of put(). I am just trying to explain why it is so.

Ali

March 29, 2023
On Wednesday, 29 March 2023 at 16:44:31 UTC, Ali Çehreli wrote:
> On 3/29/23 09:27, Salih Dincer wrote:
>
> > In this way,
> > it could also be used directly with slices. For example:
>
> > auto put(R)(R[] range, R[] source)
> >    => putImpl(range, source);
>
> That's for rvalues.
>
> > auto put(R)(ref R[] range, R[] source)
> >    => putImpl(range, source);
>
> That's for lvalues.
>
> If you are proposing keeping the current ref-taking Phobos put() as well, then the following call would be ambiguous:
>
>   slice.put([2]);  // Compilation ERROR

As far as I understand, you're saying that we cannot overload on `ref`. But we can. Salih's code demonstrates just that.

void f(ref int x) {}
void f(int x) {}
void main() { int x; f(x); f(42); } /* no errors */
March 29, 2023
On 3/29/23 12:21, ag0aep6g wrote:

> As far as I understand, you're saying that we cannot overload on `ref`.
> But we can. Salih's code demonstrates just that.
>
> void f(ref int x) {}
> void f(int x) {}
> void main() { int x; f(x); f(42); } /* no errors */

I thought Salih was proposing two more overloads to the existing put(). When I copy the existing put(), which takes 'ref R', not R[], then the code does not compile:

auto put(R)(R[] range, R[] source)
  => putImpl(range, source);

auto put(R)(ref R[] range, R[] source)
  => putImpl(range, source);

void putImpl(R)(ref R[] range, R[] source)
{
  assert(source.length <= range.length);
  foreach(element; source)
  {
    range[0] = element;  // range.front()
    range = range[1..$]; // range.popFront()
  }
}

void put(R, E)(ref R r, E e)
{
  // This is from Phobos <-------
}

void main() {
  enum data = [1, 0, 0, 4];
  auto arr = data;
  auto slice = arr[1..$-1];

  slice.put([2]);              // <-- ERROR
  assert(arr == [1, 2, 0, 4]);

  slice.put([3]);
  assert(arr == [1, 2, 3, 4]);

  arr[1..$-1].put([0, 0]);
  assert(arr == data);
}

Ali

March 29, 2023
On Wednesday, 29 March 2023 at 19:49:47 UTC, Ali Çehreli wrote:
> I thought Salih was proposing two more overloads to the existing put(). When I copy the existing put(), which takes 'ref R', not R[], then the code does not compile:
>
> auto put(R)(R[] range, R[] source)
>   => putImpl(range, source);
>
> auto put(R)(ref R[] range, R[] source)
>   => putImpl(range, source);
>
[...]
>
> void put(R, E)(ref R r, E e)
> {
>   // This is from Phobos <-------
> }

I understand Salih's ref `put` to be same as yours: a stand-in for the one that is already in Phobos. So he's proposing to add the non-ref one.

But regardless of Salih's exact intent, the broader point is: a non-ref overload could be added to Phobos. And that would enable `a[1..$-1].phobos_put([2, 3])`. Which is what he asked about originally.
March 29, 2023

On Wednesday, 29 March 2023 at 19:49:47 UTC, Ali Çehreli wrote:

>

On 3/29/23 12:21, ag0aep6g wrote:

>

As far as I understand, you're saying that we cannot overload
on ref.
But we can. Salih's code demonstrates just that.

void f(ref int x) {}
void f(int x) {}
void main() { int x; f(x); f(42); } /* no errors */

I thought Salih was proposing two more overloads to the existing put(). When I copy the existing put(), which takes 'ref R', not R[], then the code does not compile:

Wait a minute, isn't copy actually a put as well? Forget about ref for a moment, please. Actually, logically, the parameters have been swapped between the two functions. Well, if we only had copy and we used put like copy, what is the need for put? Examples are below: :)

//version = simple;/*
version = standart;//*/

version(simple) {
  auto put(R)(R[] range, R[] source)
    => copyImpl(source, range);

  auto copy(R)(R[] source, R[] range)
    => copyImpl(source, range);

  auto copyImpl(R)(R[] source, R[] range)
  {
    assert(source.length <= range.length);
    foreach(element; source)
    {
      range[0] = element;  // range.front()
      range = range[1..$]; // range.popFront()
    }
    return range;
  }

  void swap (ref int x, ref int y)
  {
    x = x ^ y;
    y = y ^ x;
    x = x ^ y;
  }
}

version(standart)
  import std.algorithm.mutation,
         std.range : put;

void main()
{
  // copy() example:
  enum len = 10;
  auto buf = new int[len]; // buffer
  auto dig = [7, 1, 2, 3]; // digits

  auto diff = len - dig.length;
  auto rem = copy(dig, buf);

  assert(buf[0..$ - diff] == [7, 1, 2, 3]);

  swap(buf[0], rem[0]);
  assert(rem == [7, 0, 0, 0, 0, 0]);

  // put() example 1:
  put(rem, [4, 5, 6, 7, 8, 9]);
  assert(buf == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);

  // put() example 2:
  enum data = [1, 0, 0, 4];
  auto arr = data;
  auto slice = arr[1..$-1];

  version(standart)
    put(slice, [2]);
  version(simple)
    slice = put(slice, [2]);
  assert(arr == [1, 2, 0, 4]);

  version(standart)
    put(slice, [3]);
  version(simple)
    slice = put(slice, [3]);
  assert(arr == [1, 2, 3, 4]);

  version(standart) {
    auto slc = arr[1..$-1];
    put(slc, [0, 0]);
  }
  version(simple)
    arr[1..$-1].put([0, 0]);
  assert(arr == data);
}

All you have to do is remove the // mark in the first line. The code compiles for me, what about you?

SDB@79

March 29, 2023
On Wednesday, 29 March 2023 at 20:29:24 UTC, ag0aep6g wrote:
> But regardless of Salih's exact intent, the broader point is: a non-ref overload could be added to Phobos. And that would enable `a[1..$-1].phobos_put([2, 3])`. Which is what he asked about originally.

Yes, that was it, but even more. Both functions serve the same thing. Why don't we combine them?

SDB@79

« First   ‹ Prev
1 2