std.v2020.algorithm etc[ WAS: Is run.d going to be expand for runtime and the phobos library?]

Joseph Rushton Wakeling joseph.wakeling at webdrake.net
Sat Jun 27 12:25:34 UTC 2020


On Friday, 26 June 2020 at 16:03:23 UTC, Paul Backus wrote:
> The fact that you, personally, cannot think of a use-case for 
> immutable ranges off the top of your head is not a strong 
> argument against supporting them. All language and library 
> features ought to work together unless there is a specific 
> reason for them not to (as is the case, for example, with 
> garbage collection and BetterC).
>
> So I think the burden of proof is on you here. If we're 
> designing a new range interface, is there a good reason it 
> *shouldn't* work with const and immutable?

There's no need to be combative here.  I'm not trying to argue a 
side, but to make sure we get clarity on what it means for ranges 
to be compatible with const and immutable in this way.

Whether we like it or not, pretty much all ranges currently work 
via some under-the-hood mutability, whether it's just updating 
the pointer of a slice, incrementing an internal variable, or 
reading from some external input into an internal buffer.

In some cases there are ready ways out of that.  For a forward 
range whose internal state is cheap to duplicate, there should be 
no problem just (automatically) making a mutable deep copy to 
iterate over.  If we're talking about a range that iterates over 
the contents of some memory locations, it seems natural enough 
that it should be possible to automatically create a mutable 
shallow copy that iterates over immutable memory locations 
(analogous to immutable(T[]) => immutable(T)[] and so on).

But -- for example -- what would it mean to have an input range 
as part of a const or immutable data structure?  Forget the 
strict interpretation of the current D keywords, let's just think 
conceptually for a moment.  Do we want to think of an immutable 
input range as just a source of input, with any under-the-hood 
mutability just an implementation detail that shouldn't be of 
interest to the external caller?  Do we consider it a 
contradiction in terms?

I'm inclined to agree with the suggestion that input ranges 
should either be non-copyable or have reference semantics, but I 
don't think that really resolves the issue given the 
transitiveness of const and immutable.

I also think it may be worth making a point of comparison to 
Rust's iterators.  Obviously Rust has a different approach to 
immutability than D -- the main concern is preventing multiple 
mutable references to the same piece of data -- but there is a 
very clear separation between iterators versus what they iterate 
over, and generally speaking (probably because of that 
separation) iterators themselves seem to be always implemented as 
mutable.

> One argument in favor of requiring mutability is that using 
> tail() instead of popFront() could result in a loss of 
> performance. I am working on investigating that claim using the 
> examples provided by Jon Degenhardt earlier in this thread.

FWIW my objection to `tail` was probably misplaced.  What I was 
interested in was coming from a completely different angle to the 
const/immutable concern.

Specifically, I'm interested in drawing a clear line between 
input ranges -- where it is not really a given that `front` can 
be pre-populated -- versus forward ranges.  For example, given a 
RandomSample, one arguably does not want `front` to be 
pre-determined on construction, but from the moment one starts 
consuming the range.  Given the current range API that means some 
unpleasant is-this-the-first-call special-casing under the hood 
of the `front` method.

So, what I'm interested in is whether we can reliably rework the 
input range API such that the only way to determine the next 
element is to actually fetch it.  That would also work with a 
`tail`-like approach if the function concerned would return 
something like:

     Tuple!(Option!(ElementType, None), Tail)

... but I'm not sure if that would interfere with other concerns 
about that approach (e.g. would it get in the way of optimizing 
performance of the recursive approach?).

> Another, which I find less convincing, is that writing 
> algorithms recursively instead of iteratively would be too 
> "difficult" or "onerous". If you have any more to add, I am 
> interested in hearing them.

I don't have specific examples, but it's worth noting that one of 
the plus points of D is that it tends to allow you to write any 
given code in the simplest and most natural way, rather than 
forcing you through more complicated paradigms for the sake of it.

So, I'd personally be reluctant to force all writers of ranges to 
write their code in a more complicated/virtuous way unless their 
use case specifically requires it.

With that in mind, it might be better to identify ways to 
simplify the _implementation_ of ranges that need to support 
const/immutable use cases.  That is, keep it pay-as-you-go -- you 
don't have to write ranges in a way that supports const/immutable 
if you don't have that use-case -- but if you do, the cost you 
have to pay is less.


More information about the Digitalmars-d mailing list