-
-
Notifications
You must be signed in to change notification settings - Fork 694
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactor std.algorithm.iteration #7638
base: master
Are you sure you want to change the base?
Conversation
Thanks for your pull request, @andralex! Bugzilla referencesYour PR doesn't reference any Bugzilla issue. If your PR contains non-trivial changes, please reference a Bugzilla issue or create a manual changelog. Testing this PR locallyIf you don't have a local development environment setup, you can use Digger to test this PR: dub run digger -- build "master + phobos#7638" |
alias _funs = AliasSeq!(_fun); | ||
} | ||
|
||
struct Result(Range) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
moved from top level
std/algorithm/iteration.d
Outdated
@@ -955,11 +920,19 @@ private: | |||
isForeachUnaryWithIndexIterable!R); | |||
|
|||
public: | |||
Flag!"each" each(Range)(auto ref Range r) | |||
if (isForeachIterable!Range || |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Largely unsuccessful attempt to make the constraint nicer. Will get back to this.
return Result!Range(range); | ||
} | ||
|
||
private struct Result(Range) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
moved from top level
return Result!Range(r); | ||
} | ||
|
||
private struct Result(Range) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
moved from top level
// Implementation of chunkBy for non-forward input ranges. | ||
private struct ChunkByImpl(alias pred, Range) | ||
private struct ChunkByImpl(alias pred, alias eq, bool isUnary, Range) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could move this inside chunkBy
as well, but it's fairly bulky.
@@ -1973,48 +1894,69 @@ if (isInputRange!Range && !isForwardRange!Range) | |||
@property auto front() | |||
{ | |||
assert(!empty, "Attempting to fetch the front of an empty chunkBy."); | |||
|
|||
static struct Result |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this was small enough to plop here
} | ||
} | ||
|
||
void popFront() | ||
{ | ||
assert(!empty, "Attempting to popFront an empty chunkBy."); | ||
openChunk = false; | ||
while (!r.empty) | ||
for (; !r.empty; r.popFront) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
my dream: that more loops are written like this
std/algorithm/iteration.d
Outdated
} | ||
} | ||
} | ||
|
||
// Single-pass implementation of chunkBy for forward ranges. | ||
private struct ChunkByImpl(alias pred, Range) | ||
private struct ChunkByImpl(alias pred, alias eq, bool isUnary, Range) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is also too bulky to move inside chunkBy
static if (isUnary) | ||
alias eq = binaryFun!((a, b) => unaryFun!pred(a) == unaryFun!pred(b)); | ||
else | ||
alias eq = binaryFun!pred; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this code was duplicated across the two implementations of ChunkByImpl
.
6968eff
to
82e8a0c
Compare
std/algorithm/iteration.d
Outdated
isRangeIterable!Range || | ||
__traits(compiles, typeof(r.front).length))) | ||
public Flag!"each" each(Range)(auto ref Range range) | ||
if (isInputRange!Range || isStaticArray!Range || hasMember!(Range, "opApply")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a simpler condition (albeit incomplete because it doesn't verify the function). Also the entire implementation is a lot shorter.
Checked the speed and memory consumed - this version is about as fast and eats 0.1% less memory.
82e8a0c
to
de48263
Compare
std/algorithm/iteration.d
Outdated
|
||
Returns: `Yes.each` if the entire range was iterated, `No.each` in case of early | ||
stopping. | ||
|
||
See_Also: $(REF tee, std,range) | ||
*/ | ||
template each(alias fun = "a") | ||
public Flag!"each" each(alias fun = "a", Range)(auto ref Range range) | ||
if (isInputRange!Range || isStaticArray!Range || hasMember!(Range, "opApply")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
whew, nice, it can be reduced to a single function
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this is something that should be celebrated.
static if (is(typeof(f1!()))) return f1();
else static if (is(typeof(f2!()))) return f2();
else static if (is(typeof(f3!()))) return f3();
else static if (is(typeof(f4!()))) return f4();
else static if (is(typeof(f5!()))) return f5();
else static if (is(typeof(f6!()))) return f6();
else static if (is(typeof(f7!()))) return f7();
else static assert(0, "Cannot iterate " ~ Range.stringof ~ " with each().");
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The "celebration" was in regard to moving each
from a member in a template to a top-level function.
I agree that the "try each pattern and see what works" strategy does raise an eyebrow, and am always on lookout for better solutions - please advise. I took inspiration from @pbackus's amazing sumtype library which works almost miraculously well at scale. There, you get to pass one or more template lambdas to a sum type and the library simply iterates all possibilities and picks the first that compiles. Experience with sumtype suggests that's a really solid idiom for D.
There are certain advantages to this strategy as implemented here compared to the existing code:
- It's easy to understand and maintain - each pattern is in the clear. This is a very important aspect! It's very difficult to make heads and tails of the code otherwise. Once you see what patterns the library is looking for, it's very easy to get into it.
- It's much more terse than the baseline - this PR deletes 166 lines, many of which I think are attributable to this change.
- There's good precedent with sumtype
de48263
to
27e293c
Compare
1392a1c
to
964a932
Compare
964a932
to
71bbed7
Compare
Good work - just a little nicer to read all over the module. Modulo getting the test suite to pass, LGTM. |
whoa blast from the past |
@andralex what are your plans with regards to this PR? |
The refactorings involve simplifying code that was working around compiler bugs, pushing symbols from top level to where they are needed, and a few more simplifications.