Thread overview
range methods on associative arrays
Jul 02, 2022
WebFreak001
Jul 02, 2022
WebFreak001
Jul 02, 2022
Paul Backus
Jul 03, 2022
monkyyy
Jul 04, 2022
bauss
July 02, 2022

a lot of range methods, such as filter, any, all, count, each, etc. would be useful on associative arrays, taking in key and value, returning a processed .byKeyValue range.

I would suggest, at least for phobos v2, we should have these functions automatically call .byKeyValue on maps and there should be support for lambdas with 2 arguments there, which automatically unwrap key and value (and possibly all tuples actually)

What do you think?

map.each!((key, value) { /* like a foreach, but functional style */ });

bool hasId = map.any!((key, value) => key == "id" && value !is null);

for this I think the implementation would basically boil down to:

  • implicitly call .byKeyValue in the map-accepting range methods
  • allow tuples and the KeyValue pair to be extended into multiple parameters on CT lambdas that have multiple arguments

Users wanting to only use keys or only values can still use .byKey or .byValue.

July 02, 2022

On 7/2/22 4:14 PM, WebFreak001 wrote:

>

a lot of range methods, such as filter, any, all, count, each, etc. would be useful on associative arrays, taking in key and value, returning a processed .byKeyValue range.

I would suggest, at least for phobos v2, we should have these functions automatically call .byKeyValue on maps and there should be support for lambdas with 2 arguments there, which automatically unwrap key and value (and possibly all tuples actually)

What do you think?

map.each!((key, value) { /* like a foreach, but functional style */ });

bool hasId = map.any!((key, value) => key == "id" && value !is null);

for this I think the implementation would basically boil down to:

  • implicitly call .byKeyValue in the map-accepting range methods
  • allow tuples and the KeyValue pair to be extended into multiple parameters on CT lambdas that have multiple arguments

Users wanting to only use keys or only values can still use .byKey or .byValue.

This idea misunderstands what a range is.

In order for an AA to be considered a "range", each popFront should remove the element from the AA.

We don't want that. Just use the range accessors.

-Steve

July 02, 2022

On Saturday, 2 July 2022 at 21:18:06 UTC, Steven Schveighoffer wrote:

>

On 7/2/22 4:14 PM, WebFreak001 wrote:

>

a lot of range methods, such as filter, any, all, count, each, etc. would be useful on associative arrays, taking in key and value, returning a processed .byKeyValue range.

I would suggest, at least for phobos v2, we should have these functions automatically call .byKeyValue on maps and there should be support for lambdas with 2 arguments there, which automatically unwrap key and value (and possibly all tuples actually)

What do you think?

map.each!((key, value) { /* like a foreach, but functional style */ });

bool hasId = map.any!((key, value) => key == "id" && value !is null);

for this I think the implementation would basically boil down to:

  • implicitly call .byKeyValue in the map-accepting range methods
  • allow tuples and the KeyValue pair to be extended into multiple parameters on CT lambdas that have multiple arguments

Users wanting to only use keys or only values can still use .byKey or .byValue.

This idea misunderstands what a range is.

In order for an AA to be considered a "range", each popFront should remove the element from the AA.

We don't want that. Just use the range accessors.

-Steve

arrays/slices are also not ranges and get special-cased (or at least have compatibility functions in std.range.primitives) to be used with range code, I don't see any reason why not to do this with maps as well, which are a first class feature of the language.

July 02, 2022

On 7/2/22 5:51 PM, WebFreak001 wrote:

>

On Saturday, 2 July 2022 at 21:18:06 UTC, Steven Schveighoffer wrote:

>

This idea misunderstands what a range is.

In order for an AA to be considered a "range", each popFront should remove the element from the AA.

We don't want that. Just use the range accessors.

arrays/slices are also not ranges and get special-cased (or at least have compatibility functions in std.range.primitives) to be used with range code, I don't see any reason why not to do this with maps as well, which are a first class feature of the language.

arrays/slices are ranges. They are actually the essential range type.

Also, an AA is fundamentally different. I can popFront on a slice, and it doesn't affect other slices. If I popFront an element from an AA, it affects all other references to that AA.

-Steve

July 02, 2022

On Saturday, 2 July 2022 at 23:13:43 UTC, Steven Schveighoffer wrote:

>

Also, an AA is fundamentally different. I can popFront on a slice, and it doesn't affect other slices. If I popFront an element from an AA, it affects all other references to that AA.

To be fair this is also true of some ranges.

The real usability issue here is that if AAs a ranges with reference semantics, then code like this

auto aa = ["foo": 123, "bar": 456];
foreach (key, val; aa)
    doSomethingWith(key, val);

...will consume the range, leaving aa empty after the loop completes. So most of the time, you will want to use .byKeyValue anyway, even if AAs implement the range interface "natively."

July 02, 2022

On 7/2/22 7:53 PM, Paul Backus wrote:

>

On Saturday, 2 July 2022 at 23:13:43 UTC, Steven Schveighoffer wrote:

>

Also, an AA is fundamentally different. I can popFront on a slice, and it doesn't affect other slices. If I popFront an element from an AA, it affects all other references to that AA.

To be fair this is also true of some ranges.

Right, but by default iterating an AA doesn't do this, and nobody would expect it.

>

The real usability issue here is that if AAs a ranges with reference semantics, then code like this

auto aa = ["foo": 123, "bar": 456];
foreach (key, val; aa)
     doSomethingWith(key, val);

...will consume the range, leaving aa empty after the loop completes. So most of the time, you will want to use .byKeyValue anyway, even if AAs implement the range interface "natively."

Technically, this doesn't use the range API.

However, aa.map!(...) would, and that's where the real problems would begin.

The proposal is somewhat cleverer in that it will implicitly call byKeyValue, but this I don't think scales well, unless the compiler is made to do it.

I think saving the call to .byKeyValue isn't worth this trouble.

-Steve

July 03, 2022

On Saturday, 2 July 2022 at 20:14:10 UTC, WebFreak001 wrote:

>

make more special cases like auto decoding

Its a general problem that ranges are bad are mimicking some types of for loop patterns; not a specific one

foreach(e;list){
  if(e.foo==cond){break;}
  e.bar;
}
//if foo is local, it may not scope correctly into std.filter`s lamda
auto store;
foreach(ref e;list){
  e=foo(e,store);
  store=bar(e,store);
  e.foobar;
}

one could argue that there are 4 useful data structures that come out of the box, static arrays, slices, strings, and aa's and that the benefit of templates is that you get (in theory) great implications when combining non-specialized data structures and algorithms; and you would be in effect suggesting the std increases its special cases to 2 out of 4. (If not 3 with slices and gc)

I suggest that aa's should follow the rough standard syntax that is the best I can mimic of defining [] to return a range for my data structures instead of the verbose thing I always have to look up. And that the algorithms the std define are robust enough to handle the general problems.

Fundamentally the std is written as if caring that ranges should be reference types; I dislike that take, but you'd be fighting an uphill battle to suggest it changes on a real level. And your suggestion would make the same sort of hacks that string has and the issues that follow.

July 04, 2022

On Saturday, 2 July 2022 at 20:14:10 UTC, WebFreak001 wrote:

>

a lot of range methods, such as filter, any, all, count, each, etc. would be useful on associative arrays, taking in key and value, returning a processed .byKeyValue range.

I would suggest, at least for phobos v2, we should have these functions automatically call .byKeyValue on maps and there should be support for lambdas with 2 arguments there, which automatically unwrap key and value (and possibly all tuples actually)

What do you think?

map.each!((key, value) { /* like a foreach, but functional style */ });

bool hasId = map.any!((key, value) => key == "id" && value !is null);

for this I think the implementation would basically boil down to:

  • implicitly call .byKeyValue in the map-accepting range methods
  • allow tuples and the KeyValue pair to be extended into multiple parameters on CT lambdas that have multiple arguments

Users wanting to only use keys or only values can still use .byKey or .byValue.

I think this confuses an associative array/hashmap with a "list" of key-value pairs; which isn't the same.

An associative array isn't really range-like in nature, if it was up to me then you shouldn't even be able to natively iterate over it in a foreach like you can now, but that's just me being too strict for the norm I guess.