Skip to content
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

Add Receiver.take_while and drop_while #354

Closed
llucax opened this issue Dec 2, 2024 · 2 comments
Closed

Add Receiver.take_while and drop_while #354

llucax opened this issue Dec 2, 2024 · 2 comments
Assignees
Labels
part:experimental Affects the experimental package resolution:wontfix This will not be worked on type:enhancement New feature or enhancement visitble to users
Milestone

Comments

@llucax
Copy link
Contributor

llucax commented Dec 2, 2024

What's needed?

Receiver.filter(predicate) is very ambiguous for people not used to functional programming, as it is never clear if the receiver should receive the messages for which predicate(message) returns True or False.

Proposed solution

Add 2 new methods to Receivers:

  • take_while(): Alias for filter().
  • drop_while(): Alias for filter(lambda m: not predicate(m))

We can also consider to deprecate filter() in the future as a way to promote more readable code.

Use cases

No response

Alternatives and workarounds

  • Add a not predicate or filterfalse method: it is more complicated and/or more work.

Additional context

The take/drop while idiom is already very widely used in many languages in conjunction with iterators/streams, like Java, Rust, Ruby and even Python itself offer those in the itertools built-in module.

@llucax llucax added part:experimental Affects the experimental package type:enhancement New feature or enhancement visitble to users labels Dec 2, 2024
@llucax llucax added this to the v1.4.0 milestone Dec 2, 2024
@shsms shsms changed the title Add Receiver.take_while and drop.while Add Receiver.take_while and drop_while Dec 2, 2024
@llucax
Copy link
Contributor Author

llucax commented Dec 4, 2024

Oh, dammit, I just realized we misinterpreted what take_while and drop_while do.

  • take_while stops the iteration as soon as one element doesn't fulfill the predicate.
  • drop_while stops using the predicate after an element fulfills the predicate.
>>> print(list(takewhile(lambda x: x < 4, [1, 2, 3, 4, 5, 6])))
[1, 2, 3]
>>> print(list(dropwhile(lambda x: x < 4, [1, 2, 3, 4, 3, 2, 1])))
[4, 3, 2, 1]

So we need new names, or forget about it / add filterfalse().

@llucax
Copy link
Contributor Author

llucax commented Dec 4, 2024

Here is a summary of what's done in different popular programming languages.

Names for filter Across Languages

Language Function Name Description
C++ std::ranges::filter Filters elements based on a predicate (C++20 and above).
Java .filter() Filters elements in a stream based on a predicate.
Dart .where() Filters elements in an iterable based on a predicate.
Rust .filter() Filters elements in an iterator based on a predicate.
Go Custom Implementation Requires manual implementation of filtering.
PHP array_filter Filters elements of an array based on a callback function.
JavaScript .filter() Creates a new array with elements that pass the predicate.
Python filter() Constructs an iterator with elements that satisfy the predicate.
Ruby .select() Filters elements of an array based on a block (alias: filter).
Kotlin .filter() Returns a list of elements satisfying the predicate.
Scala .filter() Returns a collection of elements satisfying the predicate.

Names for "Not Filter" Across Languages

Language Explicit Name Example for Negated Filter Using Filter-like Function
C++ None std::ranges::views::filter(range, [&](auto x) { return !predicate(x); });
Java None list.stream().filter(x -> !predicate(x)).collect(Collectors.toList());
Dart None iterable.where((x) => !predicate(x));
Rust None `iterator.filter(
Go None Custom implementation required (negate the predicate manually).
PHP None array_filter($array, fn($x) => !$predicate($x));
JavaScript None array.filter(x => !predicate(x));
Python None filter(lambda x: not predicate(x), iterable) or [x for x in iterable if not predicate(x)]
Ruby .reject() `array.reject {
Kotlin .filterNot() list.filterNot { predicate(it) }
Scala .filterNot() list.filterNot(predicate)

It seems like filter() is the most popular by far. I like where() better, but I'm not sure if it is worth a name change for such a small gain.

Also reject() sounds OK, but it is not popular at all and having a negative filter doesn't seem to be popular at all, and considering that we can't do type narrowing with negative filters, I'm also considering fully dropping the idea of a negative version of filter.

Conclusion

Considering all of the above, I would close this as WONTFIX 🤷

@llucax llucax self-assigned this Dec 4, 2024
@llucax llucax closed this as not planned Won't fix, can't repro, duplicate, stale Dec 4, 2024
@llucax llucax added the resolution:wontfix This will not be worked on label Dec 4, 2024
@llucax llucax modified the milestones: v1.4.0, Dropped Dec 5, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
part:experimental Affects the experimental package resolution:wontfix This will not be worked on type:enhancement New feature or enhancement visitble to users
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant