March 29, 2023

On 3/29/23 4:29 PM, 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.

I think the idea of requiring ref output ranges is that you can then let the range keep track of its output state.

An input range with lvalue elements is therefore an output range, but only if it's accepted via ref, since it has to be iterated as it goes. If you iterate it only internally, then it's either in an undetermined state when you exit put, or it is a forward range that was copied without using save.

It's not the greatest situation. I feel like we probably shouldn't have made lvalue input ranges be output ranges automatically.

-Steve

March 30, 2023

On Wednesday, 29 March 2023 at 20:50:04 UTC, Steven Schveighoffer wrote:

>

On 3/29/23 4:29 PM, 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.

I think the idea of requiring ref output ranges is that you can then let the range keep track of its output state.

So why not copying the range to a static array? The error during compilation of the code is as follows:

>

onlineapp.d(6): Error: none of the overloads of template std.algorithm.mutation.copy are callable using argument types !()(int[], int[8])
/dlang/dmd/linux/bin64/../../src/phobos/std/algorithm/mutation.d(367): Candidate is: copy(SourceRange, TargetRange)(SourceRange source, TargetRange target)
with SourceRange = int[], TargetRange = int[8]
must satisfy the following constraint:
isOutputRange!(TargetRange, ElementType!SourceRange)

import std.algorithm.mutation : copy;
void main()
{
  int[8] buf;
  auto dig = [1, 2, 3, 4];
  auto rem = dig.copy(buf);
  assert(rem.length == 4);
}

Looks like 'copy' has the same overload issue.

SDB@79

March 30, 2023

On 3/29/23 11:01 PM, Salih Dincer wrote:

>
import std.algorithm.mutation : copy;
void main()
{
   int[8] buf;
   auto dig = [1, 2, 3, 4];
   auto rem = dig.copy(buf);
   assert(rem.length == 4);
}

Looks like 'copy' has the same overload issue.

A static array is not a range of any kind. What would popFront do on a static array, since the length is part of the type?

But you can do dig.copy(buf[]) since a dynamic array is.

-Steve

March 30, 2023

On Wednesday, 29 March 2023 at 20:50:04 UTC, Steven Schveighoffer wrote:

>

On 3/29/23 4:29 PM, 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.

I think the idea of requiring ref output ranges is that you can then let the range keep track of its output state.

An input range with lvalue elements is therefore an output range, but only if it's accepted via ref, since it has to be iterated as it goes. If you iterate it only internally, then it's either in an undetermined state when you exit put, or it is a forward range that was copied without using save.

It's not the greatest situation. I feel like we probably shouldn't have made lvalue input ranges be output ranges automatically.

It should be fine to have both a ref and non-ref overload for put, though, right? If the non-ref overload is only called with rvalues, then it's fine to leave them in an undetermined state, because nothing can access them afterward anyway.

March 30, 2023

On Thursday, 30 March 2023 at 13:27:33 UTC, Steven Schveighoffer wrote:

>

But you can do dig.copy(buf[]) since a dynamic array is.

Apparently, we will not be able to get rid of the necessity of using slices. 😀 Neither with "copy" nor with "put"...

import std.algorithm.mutation : copy;
import std.range.primitives : put;

void main()
{
  int[8] buf;// = new int[8];
  auto dig = [1, 2, 3, 4];

  //dig.copy(buf[2..$-2]);/*
  auto slice = buf[2..$-2];
  dig.copy(slice);
  
  assert(buf == [0, 0, 1, 2, 3, 4, 0, 0]);
  buf = 0;
  slice.put(dig);//*/
  assert(buf == [0, 0, 1, 2, 3, 4, 0, 0]);
}

SDB@79

March 30, 2023

On 3/30/23 11:44 AM, Paul Backus wrote:

>

It should be fine to have both a ref and non-ref overload for put, though, right? If the non-ref overload is only called with rvalues, then it's fine to leave them in an undetermined state, because nothing can access them afterward anyway.

There's a certain attempt in phobos in some places to try and ensure code that is going to confuse will not compile. I think this is one of those attempts.

Consider that if you pass a slice into put, then it returns nothing. There is no indication of what actually was written. It's essentially an inconclusive call, because the "result" is the output range itself. How many elements were written? You can't tell.

I'd argue that the way input ranges are used as output ranges today is extremely confusing. It makes sort of a logical sense, but the fact that you need to store your "original" range, and then do some length math to figure out what was written makes such code very awkward all around. The output is decipherable, but not obvious.

I stand by my assertion that probably lvalue input ranges should never have been treated as output ranges implicitly. They should have had to go through some sort of wrapper.

-Steve

March 31, 2023

On Friday, 31 March 2023 at 02:23:29 UTC, Steven Schveighoffer wrote:

>

There's a certain attempt in phobos in some places to try and ensure code that is going to confuse will not compile. I think this is one of those attempts.

Consider that if you pass a slice into put, then it returns nothing. There is no indication of what actually was written. It's essentially an inconclusive call, because the "result" is the output range itself. How many elements were written? You can't tell.

This is a general issue with the put interface—it has no standardized way to report failure or partial success to the caller. And the problem applies to both lvalue and rvalue output ranges. For example:

void main()
{
    import std.stdio, std.range;

    auto sink = stdout.lockingTextWriter;
    put(sink, "hello");
}

How many characters were written? Nobody knows! Maybe you can find out with File.tell, if the file is seekable, but there's no guarantee it is. And if you're working in a generic context, where you don't know the specific type of output range you're dealing with? Forget it.

>

I'd argue that the way input ranges are used as output ranges today is extremely confusing. It makes sort of a logical sense, but the fact that you need to store your "original" range, and then do some length math to figure out what was written makes such code very awkward all around. The output is decipherable, but not obvious.

I stand by my assertion that probably lvalue input ranges should never have been treated as output ranges implicitly. They should have had to go through some sort of wrapper.

IMO this is downstream of the issue with put. If put itself returned information about what was written, then there would be no need to try and recreate that information via side channels like this (or like File.tell).

I agree that there are ways we could change things to make the side channels easier to use, but ultimately that's treating the symptom, not the disease.

1 2
Next ›   Last »