Reimplementing the bulk of std.meta iteratively

Jackel jackel894_394 at gmail.com
Tue Sep 29 09:57:47 UTC 2020


On Tuesday, 29 September 2020 at 03:14:34 UTC, Andrei 
Alexandrescu wrote:
> Or take the foreach statement. Through painful trial and error, 
> somebody figured out all possible shapes of foreach, and 
> defined `each` to support most:
>
> https://github.com/dlang/phobos/blob/master/std/algorithm/iteration.d#L904
>
> What should have been a simple forwarding problem took 190 
> lines that could be characterized as very involved. And mind 
> you, it doesn't capture all cases because per 
> https://github.com/dlang/phobos/blob/master/std/algorithm/iteration.d#L1073:
>
> // opApply with >2 parameters. count the delegate args.
> // only works if it is not templated (otherwise we cannot count 
> the args)
>
> I know that stuff (which will probably end on my forehead) 
> because I went and attempted to improve things a bit in 
> https://github.com/dlang/phobos/pull/7638/files#diff-0d6463fc6f41c5fb774300832e3135f5R805, which attempts to simplify matters by reducing the foreach cases to seven shapes.
>
> To paraphrase Alan Perlis: "If your foreach has seven possible 
> shapes, you probably missed some."
>
> Over time, things did get better. We became adept of things 
> such as lowering, and we require precision in DIPs.

I think this is a mischaracterization of a problem. I'm not 
really a fan of how ranges are implemented, especially how UFCS 
is used to chain functions together. When they start to become 
10+ lines of a chain it is almost impossible to profile such 
code; with tools. Let alone try to understand what is happening, 
it would require to know how each function is implemented and how 
it expands and ends up being executed. Some things are lazy, 
somethings aren't; eg requiring `.array` after a `.map`. This is 
where I feel there shouldn't even be a `each` to begin with, just 
use a for loop.

Now that's my opinion, I deal with low level code that needs to 
be optimized and those UFSC chains are likened to scripting 
languages that usually don't need to worry about optimization. 
For short chains I don't think it's that big of a deal, it's 
usually easy enough to figure out what is going on and not enough 
is happening for too much to go wrong.

> And mind you, it doesn't capture all cases because per 
> https://github.com/dlang/phobos/blob/master/std/algorithm/iteration.d#L1073:
>
> // opApply with >2 parameters. count the delegate args.
> // only works if it is not templated (otherwise we cannot count 
> the args)

If you use a template of `opApply()` you lose type inference (as 
you do with regular templates). It's a limitation of the feature. 
You'd have the same problem if you have an overload of 
`opApply()` with delegates that have the same number of 
arguments; it won't know which one to pick. If you want to 
support both you'd just need 1 other case, but honestly if you 
have a template `opApply`, that's just bad practice (due to 
losing type inference). It doesn't need to be supported. The only 
reason I've ever wanted a templated opApply() is for auto 
attribute inference, but that's not worth losing the auto type 
inference you get with foreach. There was a suggestion for adding 
a new feature, or rather altering the behavior to allow for both, 
but I don't think it was well received.


> I know that stuff (which will probably end on my forehead) 
> because I went and attempted to improve things a bit in 
> https://github.com/dlang/phobos/pull/7638/files#diff-0d6463fc6f41c5fb774300832e3135f5R805, which attempts to simplify matters by reducing the foreach cases to seven shapes.

You are going the wrong way about simplifying the code. Why not 
add an opApply() property to the internal container types (array, 
assoc array, etc...)? Shouldn't be that difficult. Cases 4, 5, 6, 
and 7 would collapse into one then.

Cases 1, 2, and 3 aren't really different ways of using foreach. 
They are just conveniences for ranges. That tuple expansion is 
odd though, doesn't seem to be documented and it's a feature that 
foreach doesn't have (unlike the second parameter for the index). 
Doesn't need to be there, but someone probably uses it somewhere 
now.

So really it's just 2 cases with an additional case to support an 
index counter. That fits the bill, case 1: foreach, case 2: 
phobos ranges.

It's not as bad as you make it out to be, for this case.


More information about the Digitalmars-d mailing list