On 3/9/23 11:44 AM, FeepingCreature wrote:
> On Thursday, 9 March 2023 at 13:16:07 UTC, Steven Schveighoffer wrote:
> On 3/9/23 3:06 AM, FeepingCreature wrote:
> Yes I know the stated reason, to save time initializing the range in filter
.
You know what I did because we didn't know filterBidirectional
existed?
I literally walked the entire range returned by filter
to find the last matching element.
Why not rng.retro.filter
?
retro
changes the semantics of the range, which often represents business data. Thinking about the underlying model is complicated enough without throwing in "But consider: what if the elements were in reverse order".
Well, walking the entire range to find the last one doesn't imply that ordering matters at all. You are just looking for the last one.
> > > filter
should expose back
. By all means, let back
lazily initialize, but I don't understand how filterBidirectional
was ever considered acceptable. Since when do we sacrifice ease of use for performance? I mean, since 2011 apparently. - This is bad ergonomics, bad discoverability and bad API design. filterBidirectional
delenda est.
This has been a constant debate -- should ranges be allowed to lazily initialize on the first call to front (or back)?
I'd say no. back/front shouldn't be modifying operations. I dislike the idea of storing a "was initialized" flag and having to check that on every call.
That being said, there's no formal requirement for it. It's just a convention I think Phobos should stick to for its included ranges.
Sure: so precompute both back
and front
, every filter
call. No caching. This isn't done right now because the language presumes, arbitrarily, that you won't care about back
. That's what I meant by sacrificing convenience for performance. Just do what filterBidirectional
does, every time, and have the current filter
be the odd one out. filterForward
or whatever.
I would be fine with that. But in truth, this is simply an acceptance of the status quo, just with different names.
> (I think the best way to go, in truth, is to preinitialize front and cache back. But that solution, much as I think it is optimal for all involved interests, is very hard to love.)
I prefer to have control over all the capabilities. If there is going to be significant cost for something I won't actually use, I'd rather avoid it.
Allowing composability might be the way to go.
> That said, I think the range API has a fundamental problem here. filter
pre-initializes front
on every access, because it reasonably presumes that we will then want to query it. However, it cannot rely on this. If we were guaranteed that every access to popFront
would alternate with at least one access to front
, and conversely back
with popBack
, we could just reduce popBack
to nextRange.popBack
and do the rest of the popping in back
! Then in the fast case (one access to each, in turn) we would not waste any check. And the actual filter
call would always be constant-time.
front
and empty
are semantically non-mutating properties. Calling front
and/or empty
as many times as you want should not alter any values until you call popFront
.
I prefer them to be actually non-mutating as well.
Delaying construction seems like the better option, and having such a wrapper solves all those problems for those who want lazy, without imposing on users who are intending to just use a range as it's intended.
-Steve