-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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 Flux.unfold
#3897
Add Flux.unfold
#3897
Conversation
@asakaev Please sign the Contributor License Agreement! Click here to manually synchronize the status of this Pull Request. See the FAQ for frequently asked questions. |
1 similar comment
@asakaev Please sign the Contributor License Agreement! Click here to manually synchronize the status of this Pull Request. See the FAQ for frequently asked questions. |
@asakaev Thank you for signing the Contributor License Agreement! |
Thanks for the proposal @asakaev. I can't say whether we'd like to integrate this, but it looks interesting as it's rather simple and encapsulates a familiar pattern. However, adding an operator opens up doors for more ideas. E.g. Would you be able to provide a few use cases comparing the usage of this new operator vs utilizing existing factories? That would be useful to guide the decision process. Thanks in advance. |
Thank you for considering my proposal! I'm excited to discuss how this aligns with the existing API. Functional approachThe current The proposed // evens [0, 2, 4, 6, ...]
Flux.unfold(0, s -> Optional.of(Tuples.of(s, s + 2)));
// fib [0, 1, 1, 2, 3, 5, 8, ...]
Flux.unfold(Tuples.of(0, 1), s -> {
var a = s.getT1();
var b = s.getT2();
return Optional.of(Tuples.of(a, Tuples.of(b, a + b)));
});
// countdown [3, 2, 1]
Flux.unfold(3, s -> s == 0 ? Optional.empty() : Optional.of(Tuples.of(s, s - 1))); Asynchronous variantState-of-the-art libraries like fs2 and zio-streams have set the pattern with abstract <A, S> Flux<A> unfoldMono(S init, Function<S, Mono<Optional<Tuple2<A, S>>>> f); However, this is beyond the scope of this PR, as Next stepsIf the proposal is interesting, I’ll add documentation and tests. For a deeper theoretical foundation, I recommend Conal Elliott’s talk on folds and unfolds. Thank you again for your consideration! |
Looks very promising. Any plans to add this functionality? |
Seems that it will be great for programmers that familiar with FP. |
Am I getting it right to compare the implementations as below? Even numbers:
Fibonacci:
Countdown:
|
Thanks for your detailed response and the examples @chemicL! You're absolutely on point with your comparisons. I'd like to emphasize a few key advantages that the Robustness & SimplicityThe Type-Safe PredictabilityThe Referential TransparencyOne of the most important benefits of Predictability: The Mental ease: Code that's referentially transparent is easier to inspect and understand at a glance. This frees developers' mental space to focus on shipping quality software instead of worrying about intricate control flows or side effects. In essence, by introducing If this direction makes sense, I’d be glad to move forward with the next steps. Thanks again for your thoughtful consideration! |
Hi @chemicL, I wanted to bring up another key aspect of this proposal. Currently, With
Flux<Integer> factorial() {
return Flux.unfold(Tuples.of(1, 1), s -> {
var n = s.getT1();
var acc = s.getT2();
return Optional.of(Tuples.of(acc, Tuples.of(n + 1, acc * (n + 1))));
});
}
Flux<Integer> collatz(Integer num) {
return Flux.unfold(num, n ->
n == 1 ?
Optional.empty() :
Optional.of(Tuples.of(n, n % 2 == 0 ? n / 2 : 3 * n + 1))
);
}
<T> Flux<T> constant(T x) {
return Flux.unfold(0, s -> Optional.of(Tuples.of(x, s)));
}
Flux<Boolean> alternate() {
return Flux.unfold(true, x -> Optional.of(Tuples.of(x, !x)));
}
Flux<Integer> powersOfTen() {
return Flux.unfold(1, x -> Optional.of(Tuples.of(x, x * 10)));
}
Flux<Integer> digits(Integer n) {
return Flux.unfold(n, x ->
x == 0 ?
Optional.empty() :
Optional.of(Tuples.of(x % 10, x / 10))
);
}
<T> Flux<T> repeatN(Integer n, T x) {
return Flux.unfold(n, i ->
i == 0 ?
Optional.empty() :
Optional.of(Tuples.of(x, i - 1))
);
}
Flux<Integer> random(Long seed) {
return Flux.unfold(new Random(seed), rng ->
Optional.of(Tuples.of(rng.nextInt(), rng))
);
} These examples show how clean and intuitive the I’m excited to hear your thoughts and hope we can take this forward. Let me know if there's anything you'd like to discuss further! |
@asakaev thank you for the thorough explanation. I've been busy with other obligations recently, but now had the time to review your points. I am convinced with the proposal and I believe it would be a handy addition to the API. My request then is - can you please enhance your PR with:
Thanks! |
Thank you for this contribution. Unfortunately, as a project stewarded by Broadcom, we are unable to accept contributions from Russian sources due to Broadcom export policy at this time. Thanks for your continued use of Spring. |
@mminella Let's check Contributor Covenant Code of Conduct.
So, @mminella could you please point us to the policy you used? What do you mean by Thank you. |
Technically for Broadcom this pull request is an import, not an export, so it's unlikely that the export policy applies in any case. |
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
Created a new PR #3921 |
This thread understandably steers towards political and emotional statements rather quickly and I'd like to add a few observations to nurture the more constructive parts of the discussion: I know there is a raging war with unimaginable suffering and uncountable deaths. There is no denying or belittling this and no "but". Please keep in mind that it's highly unlikely the decission to close this PR was meant as a political statement by those who made it. This decission has the smell of corporate legal and CYA all over it. It's highly probable that @mminella is just the bearer of news and chances of resolving this issue in discussion with him are slim. I assume the obvious road block in merging this PR is the plain visibility of its authors whereabouts in his profile. I can't imagine any realistic way of preventing people with a certain nationality from OSS projects like Spring other than looking for obvious signs in there publicly available profile. So the factual consequence of the corporate decision to close PRs based on these criteria seems superficial at best. That said, I also can't imagine just re-opening this contribution from another account would resolve any legal issue since I assume the copyright/intellectual property is obviously still @asakaev's which seems to be the actual issue. I'm no lawyer, but it's hard to imagine it would be so easy to resolve this issue. |
Summary
This PR introduces a new
unfold
operator toFlux
, providing a functional approach for generating a sequence from an initial seed value. Theunfold
method offers a declarative alternative to the existinggenerate
operator, inspired by similar functionality in Scala and Haskell.Motivation
The
unfold
operator enhances the API by allowing users to construct sequences in a functional style, which may be more familiar to developers coming from FP-centric languages. While functionally similar togenerate
,unfold
focuses on a clean, declarative approach to defining recursive sequences.