Skip to content

Comments

Make each capable of early stopping#5268

Merged
andralex merged 5 commits intodlang:masterfrom
andralex:each-with-early-stopping
Aug 15, 2018
Merged

Make each capable of early stopping#5268
andralex merged 5 commits intodlang:masterfrom
andralex:each-with-early-stopping

Conversation

@andralex
Copy link
Member

Flag to the rescue...

@andralex
Copy link
Member Author

Code is a tad repetitive, ideas welcome on how to simplify.

@andralex andralex changed the title Make each capable of early stopping Make each capable of early stopping Mar 12, 2017

Normally the entire range is iterated. If partial iteration (early stopping) is
desired, `fun` needs to return a value of type $(REF Flag,
std.typecons)`!"each"` (`No.each` to continue iteration, or `No.each` to stop
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if I specify Yes.each ?


Params:
pred = predicate to apply to each element of the range
r = range or iterable over which each iterates
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

quote each

iteration).

Params:
pred = predicate to apply to each element of the range
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's called fun, not pred

while (!r.empty)
{
cast(void) unaryFun!pred(r.front);
static if (!is(typeof(unaryFun!fun(r.front)) == Flag!"each"))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

swap the then and else clauses to eliminate the !

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nah, then I have a dangling else and need to add gnarly {}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ! is a cognitive problem. Negations in general should be avoided. I missed it myself the first time through, and got mildly confused for a moment.

while (!r.empty)
{
cast(void) binaryFun!BinaryArgs(i, r.front);
static if (!is(typeof(binaryFun!BinaryArgs(i, r.front)) ==
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

cast(void) binaryFun!BinaryArgs(i, r.front);
static if (!is(typeof(binaryFun!BinaryArgs(i, r.front)) ==
Flag!"each"))
cast(void) binaryFun!BinaryArgs(i, r.front);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure what the cast(void) is good for

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was there. Chesterton's fence...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a common stylistic practice in other languages, in some it is required. The function shouldn't be returning a significant value anyway.

Copy link
Member

@MetaLang MetaLang Mar 12, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's required if binaryFun is a strongly pure function and has a return type of void.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MetaLang hmmm, yah but in that case it seems the cast precludes a potentially useful warning. This also ties in somehow with @schveiguy's discussion of free.

Copy link
Member

@MetaLang MetaLang Mar 12, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Depends on whether you want each to only accept non-strongly-pure functions or not. My first thought is that it's fine because why would you use each if you don't want to do something impure? However, if you want each to be as close in functionality to foreach as possible, then strongly-pure functions should be allowed as it is perfectly acceptable to have a "pure" foreach loop:

foreach (i; [0, 1, 2])
{
    //This loop has no side-effects
}

That said, if I understand how strong-pure inference works, this code should not be accepted by the current DMD:

[0, 1, 2].each!((i) {}); //Should not compile

However, this code is accepted by DMD 2.037.2, so it looks like a non-issue (and now makes me wonder why this is accepted). If a problem ever does come up we can deal with it then.

auto result = Yes.each;
auto dg(Parameters!(Parameters!(r.opApply)) params)
{
static if (!is(typeof(binaryFun!BinaryArgs(i, e)) == Flag!"each"))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is(typeof(binaryFun!BinaryArgs(i, e)) == Flag!"each") appears repeatedly. Factor it out and use it to initialize a manifest constant.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How? Zat iz ze queschiohn.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

enum isEach = is(typeof(binaryFun!BinaryArgs(i, e)) == Flag!"each");
...
static if (isEach)
    ...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sehr gut, ja?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@WalterBright there is no improvement - essentially this introduces one additional name that is used only once. No?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Niet...

(isForeachUnaryIterable!R || isForeachBinaryIterable!R);

void each(Range)(Range r)
Flag!"each" each(Range)(Range r)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using static if instead of these overloads.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They don't appear in the docs so choosing one or another is a wash. I preserved the existing structure.

@JackStouffer
Copy link
Contributor

What's the use case?

@wilzbach
Copy link
Contributor

@andralex FYI: there are still multiple issues with each, but currently I lack the time and laptop to hunt the cycle issue down.
-> #4990

@quickfur
Copy link
Member

ping @andralex @WalterBright

What's needed to get this moving again?

@dlang-bot
Copy link
Contributor

Thanks for your pull request, @andralex!

Bugzilla references

Your 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 locally

If you don't have a local development environment setup, you can use Digger to test this PR:

dub fetch digger
dub run digger -- build "master + phobos#5268"

@wilzbach
Copy link
Contributor

What's needed to get this moving again?

Someone to revive it from the dead and rebase to latest, greatest master. I just did so ;-)

@PetarKirov
Copy link
Member

@wilzbach needs another rebase

@wilzbach wilzbach force-pushed the each-with-early-stopping branch 2 times, most recently from b1701ac to 005a391 Compare August 15, 2018 12:19
is(typeof((R r) {
foreach (i, ref a; r)
cast(void) unaryFun!pred(i, a);
cast(void) binaryFun!BinaryArgs(i, a);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was incorrect.

}
else
{
if (unaryFun!fun(r.front) == No.each) return No.each;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The braces are required to avoid DScanner's "Mismatched static if. Use 'else static if' here." warning.

@wilzbach
Copy link
Contributor

needs another rebase

Done. I also found a small issue with the newly added isForeachUnaryWithIndexIterable

@wilzbach wilzbach force-pushed the each-with-early-stopping branch from 005a391 to 6ef73fb Compare August 15, 2018 13:04
@andralex
Copy link
Member Author

@wilzbach did you adopt this PR? If so, how does that work? Thx!

@wilzbach
Copy link
Contributor

wilzbach commented Aug 15, 2018

@wilzbach did you adopt this PR?

Yes - it's all green now =)

If so, how does that work? Thx!

I use my pr_push wrapper ;-)

@andralex andralex merged commit a12d990 into dlang:master Aug 15, 2018
@andralex
Copy link
Member Author

I guess it's legal to merge then!

@wilzbach
Copy link
Contributor

Ah I still wanted to squash the commits, but I guess it's too late now :/

BTW I realized that it might be nice to highlight this change in the release changelog and submitted a changelog entry for it: #6668

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants