November 14, 2018
On Wednesday, 14 November 2018 at 15:33:49 UTC, rikki cattermole wrote:
> On 15/11/2018 4:07 AM, lagfra wrote:
>> https://www.reddit.com/r/cpp/comments/9vwvbz/2018_san_diego_iso_c_committee_trip_report_ranges/
>> 
>> 
>> By 2020 C++ is planning to introduce:
>> 
>> * Ranges
>
> Really butchered. From what I can see they never mentioned D in any of the documents (kinda glad tbh). Those documents even question what it should be doing...
>
> And the example code... yikes.
> No way that is going to be used.
>
>> * Contracts
>> * Concepts (`__traits`)
>> * Proper constexpr
>> * Modules
>> * Reflections
>> * Green threads
>
> Skepticism especially when there is 2023 being listed as a conservative estimate on the Reddit post.

In no way this is the usual trollpost (I am a participant of SAoC). What bugs me is the shortening distance regarding what D has to offer with respect to C++. While D for sure has a way better syntax (thinking of template declarations, `immutable`, UDAs) and a GC, what are the advantages of using D vs C++ if
November 14, 2018
On Wednesday, 14 November 2018 at 15:07:46 UTC, lagfra wrote:
> By 2020 C++ is planning to introduce:
>
> * Ranges

Still no slicing, right? array[1 .. $-1] is just so handy.

> [...]
> Right now it already has:
>
> * `auto` variables

I hate having to type `const auto bla = ...`. With D, it's just `const bla = ...`.

> * Ranged for (`foreach`)

But not an implicit 2nd version with index. `foreach (i, e; range) { ... }` comes in handy if you need the index too.

> * Lambda expressions and closures

const auto lambda = [...](T param) { ... };
In D, that can be `(T param) => <expression>` or a nested function, both much more readable.

> * `nothrow` attributes
> * Proper containers
> * Proper RAII

But no distinction between value and reference types, i.e., D's distinction between structs and classes.

> TL;DR: what will D offer with respect to C++ when almost all key features of D are present in C++20(+)?

Some other things coming to mind OTOH:

* *Much* saner templates, no std::enable_if<> crap etc., and of course static if. static foreach may also be interesting in some cases.
* Builtin associative arrays. std::unordered_map<Key, Value> => Value[Key].
* Sane basic types. No need to include a header for the definition of `size_t`, no distinct basic types overlap (C++ long, wchar_t etc.; not 4 distinct types for a byte - C++ `char`, `signed char`, `unsigned char`, and planned `utf8_t`].
* `alias this` for simple decorators without boilerplate at all.
* Pretty good interop with C++, and that's constantly getting better.
* User-defined attributes.
* Built-in unittests and documentation.

I surely missed some other cool features.
November 14, 2018
On 11/14/18 11:09 AM, Eugene Wissner wrote:
> On Wednesday, 14 November 2018 at 15:49:48 UTC, jmh530 wrote:
>> On Wednesday, 14 November 2018 at 15:33:49 UTC, rikki cattermole wrote:
>>> [snip]
>>>
>>> Really butchered. From what I can see they never mentioned D in any of the documents (kinda glad tbh). Those documents even question what it should be doing...
>>>
>>
>> I recall D being briefly mentioned in the Range specification. They rejected D's approach because they wanted to build on existing iterator-based code.
> 
> No, it wasn't the reason. Some algorithms cannot be implemented with ranges as efficient as with iterators.
> 
> "In other words, by converting the is_word_boundary from iterators to D-style ranges, the algorithm goes from O(1) to O(N). From this we draw the following conclusion: D’s choice of algorithmic basis operations is inherently less efficient than C++’s."
> 
> C++ iterators are more flexible. I think of things like rotate and bringToFront - in C++ you need begin, end and middle iterators. In D you can't have something like a "middle", you have to pass two independent ranges. But since most algorithms require begin and end iterators I like D ranges because they aren't that verbose. But D ranges aren't always nicer, for example SList.insertAfter requires a hack with accepting Take!Range instead of just Range.

The solution is cursors -- a "range" of a single element, which points at that element. You can iterate it just like a range (it has front, popFront, and empty), but it's used basically to mark the location you are interested in.

dcollections used cursors, which I found much nicer. For example, you can run a search on a tree, and get a reference to a single node, instead of a range. You can then compose ranges using cursors for any desired subsection:

auto allElementsBefore = tree[tree.begin .. tree.find("someValue")];
auto allElementsAfter = tree[tree.find("someValue") .. $];
auto elementsBetween = tree[tree.find("value1") .. tree.find("value2")];

insert just takes a cursor, everything is good.

-Steve
November 14, 2018
On Wednesday, 14 November 2018 at 17:22:50 UTC, kinke wrote:
> I surely missed some other cool features.

I forgot to mention the unified function call syntax, which allows for much more readable and compact code; that's definitely worth mentioning too.
November 14, 2018
On Wednesday, 14 November 2018 at 15:07:46 UTC, lagfra wrote:
> https://www.reddit.com/r/cpp/comments/9vwvbz/2018_san_diego_iso_c_committee_trip_report_ranges/
>
> By 2020 C++ is planning to introduce:
>
> * Ranges
> * Contracts
> * Concepts (`__traits`)
> * Proper constexpr
> * Modules
> * Reflections
> * Green threads
>
> Right now it already has:
>
> * `auto` variables
> * Ranged for (`foreach`)
> * Lambda expressions and closures
> * `nothrow` attributes
> * Proper containers
> * Proper RAII
>
> In no way this is the usual trollpost (I am a participant of SAoC). What bugs me is the shortening distance regarding what D has to offer with respect to C++. While D for sure has a way better syntax (thinking of template declarations, `immutable`, UDAs) and a GC, what are the advantages of using D vs C++ if my goal is to build a complex system / product?
>
> TL;DR: what will D offer with respect to C++ when almost all key features of D are present in C++20(+)?

newCTFE will blow away constexpr in perfomance!

and when I have time for it, D will get type-manipulation abilities which are much more efficient than what you can do with templates.

it'll be faster to compile.
November 14, 2018
On Wednesday, 14 November 2018 at 16:09:32 UTC, Eugene Wissner wrote:
> No, it wasn't the reason. Some algorithms cannot be implemented with ranges as efficient as with iterators.
>
> "In other words, by converting the is_word_boundary from iterators to D-style ranges, the algorithm goes from O(1) to O(N). From this we draw the following conclusion: D’s choice of algorithmic basis operations is inherently less efficient than C++’s."
>
> C++ iterators are more flexible.

You meant D ?

> I think of things like rotate and bringToFront ...

November 14, 2018
On Wed, 14 Nov 2018 16:09:32 +0000, Eugene Wissner wrote:
> No, it wasn't the reason. Some algorithms cannot be implemented with ranges as efficient as with iterators.
> 
> "In other words, by converting the is_word_boundary from iterators to D-style ranges, the algorithm goes from O(1) to O(N). From this we draw the following conclusion: D’s choice of algorithmic basis operations is inherently less efficient than C++’s."

The code in question:

```
bool is_word_boundary(Range1, Range2)( Range1 front, Range2 back )
    if (isBidirectionalRange!Range1 && isInputRange!Range2 )
{
    bool is_word_prev = front.empty ? false : isword(front.back);
    bool is_word_this = back.empty ? false : isword(back.front);
    return is_word_prev != is_word_this;
}

range r = myrange;
size_t n = 0;
for(range r = myrange; !r.empty; r.popFront(), ++n)
     if( is_word_boundary( takeExactly(myrange, n), r) )
        break;
```

This is fine for a random access range (takeExactly yields a random access range from it). But with a bidirectional range like a doubly linked list, takeExactly gives you a forward range; you can't efficiently construct `.back()` for it.

If we had a notion of splittable ranges, that would work. (All deterministic ranges are splittable, but unless the range provides a special implementation or is a random access range, it's O(N) to split.)

If subranges were a dedicated concept, that would also work; you could use a subrange to iterate through the outer range.

And as Steven Schveighoffer mentioned, generalizing indexes allows a lot of range types to be close enough to random access to make this use case efficient. I'm pretty sure this should be equal to C++ iterators.
November 14, 2018
On Wednesday, 14 November 2018 at 17:47:10 UTC, Basile B. wrote:
> On Wednesday, 14 November 2018 at 16:09:32 UTC, Eugene Wissner wrote:
>> No, it wasn't the reason. Some algorithms cannot be implemented with ranges as efficient as with iterators.
>>
>> "In other words, by converting the is_word_boundary from iterators to D-style ranges, the algorithm goes from O(1) to O(N). From this we draw the following conclusion: D’s choice of algorithmic basis operations is inherently less efficient than C++’s."
>>
>> C++ iterators are more flexible.
>
> You meant D ?

C++. An iterator points to some element in a range, and you can mix it as you need. In D you always have two iterators: beginning and the end of the range. The feature I miss most, is a bidirectional iterator. In C++ you can go back and forth with this iterator. In D if you do popBack there is no way back (or front :)); with popFront you can't reach the element after back, even if there such elements, you have to take an another range from the container.

D ranges are more compact, because you need the begining and the end in the most cases, so such functions just take one range instead of two iterators. But there are still a lot of cases where D ranges don't work.

Steven Schveighoffer mentioned an interesting solution but it adds complexity: Implement Range, ConstRange, Cursor, ConstCursor?, algorithms that accept ranges or cursors and so forth...

>> I think of things like rotate and bringToFront ...


November 14, 2018
On 11/14/18 1:08 PM, Eugene Wissner wrote:
> On Wednesday, 14 November 2018 at 17:47:10 UTC, Basile B. wrote:
>> On Wednesday, 14 November 2018 at 16:09:32 UTC, Eugene Wissner wrote:
>>> No, it wasn't the reason. Some algorithms cannot be implemented with ranges as efficient as with iterators.
>>>
>>> "In other words, by converting the is_word_boundary from iterators to D-style ranges, the algorithm goes from O(1) to O(N). From this we draw the following conclusion: D’s choice of algorithmic basis operations is inherently less efficient than C++’s."
>>>
>>> C++ iterators are more flexible.
>>
>> You meant D ?
> 
> C++. An iterator points to some element in a range, and you can mix it as you need. In D you always have two iterators: beginning and the end of the range. The feature I miss most, is a bidirectional iterator. In C++ you can go back and forth with this iterator. In D if you do popBack there is no way back (or front :)); with popFront you can't reach the element after back, even if there such elements, you have to take an another range from the container.

What you need is an index in a range. The inherent thing about a naked iterator (bidirectional or otherwise) is it's not safe. It's super easy to get into UB territory.

> D ranges are more compact, because you need the begining and the end in the most cases, so such functions just take one range instead of two iterators. But there are still a lot of cases where D ranges don't work.

It's also much easier to avoid invalid situations with a range vs. carrying around 2 iterators. Indeed there are very few cases for algorithms that need 3 reference points. But where iterators are great are simply as a pointer to an item in a container (see below).

> 
> Steven Schveighoffer mentioned an interesting solution but it adds complexity: Implement Range, ConstRange, Cursor, ConstCursor?, algorithms that accept ranges or cursors and so forth...

Ugh, that's an issue with D and tail-const. You really should never need multiple functions for those things, it should just be Range and Cursor, and the tail-modified versions should be handled by the language.

But one thing cursors (at least the ones I implemented) don't solve is the bidirectional nature -- a cursor is inherently not really movable. It needs boundaries to prevent Bad Things, and existentially you don't *want* boundaries for a cursor, because boundaries change.

The most common use case I had for a cursor was keeping a reference to an element in a container. At various points, I wanted to refer to that element to use it, possibly to restructure the container, I didn't want to have to search for it again. But if you use an actual range for this, you now have *two* elements you are pointing at (the beginning and the end). It's entirely possible for that relationship to change or become invalid. With a cursor, it can't become invalid because you are only pointing at one spot.

-Steve
November 14, 2018
On Wednesday, 14 November 2018 at 17:33:27 UTC, Steven Schveighoffer wrote:
> [snip]
>
> The solution is cursors -- a "range" of a single element, which points at that element. You can iterate it just like a range (it has front, popFront, and empty), but it's used basically to mark the location you are interested in.
>
> dcollections used cursors, which I found much nicer. [snip]

I always thought the cursor approach you've discussed before was interesting, but I never played around with it much myself.

It looks to me like your cursor implementation operates similar to Optional types (as in the optional package). Does that make sense?