I am trying to use a sequence range as a sorted range so that I can apply a search on it. For instance this might be used to implement integer square root as so:

```dlang
auto square(N)(N n) {
    return n * n;
}

auto isqrt(int n) {
    import std.range: sequence, assumeSorted;

    auto seq = sequence!((a, n) => square(n));
auto seqAsSorted = seq.assumeSorted!((a, b) => a <= b); // fails to compile
    auto lowerBounds = seqAsSorted.lowerBound(n);
    auto ret = lowerBounds.length;
    return ret;
}
```

The assumeSorted line which creates a SortedRange fails to compile (dmd v2.100.1)

I get the following error instead:
```
/usr/local/opt/dmd/include/dlang/dmd/std/range/package.d(10896): Error: generated function `repro_range_sorted.isqrt.Sequence!(__lambda2, Tuple!()).Sequence.opAssign(Sequence!(__lambda2, Tuple!()) p)` is not callable using argument types `(Take!(Sequence!(__lambda2, Tuple!())))` /usr/local/opt/dmd/include/dlang/dmd/std/range/package.d(10896): cannot pass argument `this._input.opSlice(a, b)` of type `Take!(Sequence!(__lambda2, Tuple!()))` to parameter `Sequence!(__lambda2, Tuple!()) p` /usr/local/opt/dmd/include/dlang/dmd/std/range/package.d(11478): Error: template instance `repro_range_sorted.isqrt.SortedRange!(Sequence!(__lambda2, Tuple!()), __lambda4, SortedRangeOptions.assumeSorted)` error instantiating /<truncated>/src/repro_range_sorted.d(22): instantiated from here: `assumeSorted!((a, b) => a <= b, Sequence!(__lambda2, Tuple!()))` Failed: ["/usr/local/Cellar/dmd/2.100.1/bin/dmd", "-v", "-o-", "/<truncated>/src/repro_range_sorted.d", "-I/<truncated>/src"]
```

From trying to understand the error, it seems that `opSlice` for `Sequence` is implemented via a `Take` which means that it returns a range of a different type than the object on which `opSlice` was called (`Sequence`). The implementation of `SortedRange` assumes that the type of the wrapped range is static, hence in its own implementation of `opSlice` it assigns the result of calling `opSlice` on the wrapped range to the variable of the original, fixed, type (here `Sequence`).

Questions:
Would this use of `SortedRange`/`Sequence` be considered conventional and something to be supported? How would you write the above code differently, in a range-like manner if not? My motivation in using `Sequence` is constant memory. For comparison here is a variant that using an array range:
```dlang
auto isqrt2(int n) {
    import std.algorithm: map;
    import std.range: array, iota, sequence, assumeSorted;

    auto seq = iota(1, n + 1).map!(square).array;
    auto seqAsSorted = seq.assumeSorted!((a, b) => a <= b);
    auto lowerBounds = seqAsSorted.lowerBound(n);
    auto ret = lowerBounds.length;
    return ret;
}
```
It is a bit surprising that `opSlice` returns an object of a different type, is this something that was designed? Are there other instances in the standard library where a range-returning method gives a different type than the type on which the method is implemented (i.e. method defined in a Range class / struct, not standalone methods like `map` and co. available within `std.algorithm`)? It would seem from the `SortedRange` example that the assumption is that methods returning a range object return an object of the same type. Is there a way to check where else this assumption is made?

Reply via email to