Oh, I realize that the old thread was made before we upgraded to Mailman 3. So, adding you @Viktor Klang <[email protected]>, in case you don't get the email otherwise.
On Wed, Aug 26, 2026 at 10:26 AM David Alayachew <[email protected]> wrote: > Hello Viktor and Rémi, > > First off, sorry for the horrifically delayed response. Juggling disasters > and emergencies. > > Rémi, thanks for the example with mapMulti. That has been my temporary > workaround for now, and while it is still not ideal, it is better than > where I was before. > > Viktor, sure, here is a simple example -- traversing a directory tree, and > only passing the files down the stream. > > I actually made a post on StackOverflow -- > https://softwareengineering.stackexchange.com/questions/461442/ > > But anyways, when attempting to do this with streams, this is where I > started. > > > import module java.base; > > void main() throws Exception > { > > final Path root = Path.of(System.getProperty("user.home")); > > IO.println(root); > IO.println(root.toAbsolutePath()); > > Stream > .of(root) > .mapMulti(this::recursiveDescent) > .limit(5) > .forEach(IO::println) > ; > > } > > private void recursiveDescent(final Path rootPath, final Consumer<Path> > downstream) > { > > final Stack<Path> stack = new Stack<>(); > stack.push(rootPath); > > while (!stack.empty()) > { > > final Path path = stack.pop(); > > if (Files.isRegularFile(path)) > { > > downstream.accept(path); > > } > > else > { > > try (final Stream<Path> folderContents = Files.list(path)) > { > > folderContents.forEach(stack::push); > > } > > catch (final Exception exception) > { > > throw new IllegalStateException("Failed for " + path, > exception); > > } > > } > > } > > } > > But this has at least 2 major downsides. > > 1 - This is not easy to turn parallel (comparatively). > > 2 - This does not short-circuit when the downstream no longer accepts > elements. > > Ok, I can at least solve problem 2 by becoming a Gatherer instead. > > Here is my gatherer attempt. > > > import module java.base; > > void main() throws Exception > { > > final Path root = Path.of(System.getProperty("user.home")); > > IO.println(root); > IO.println(root.toAbsolutePath()); > > final Gatherer<Path, Stack<Path>, Path> gatherer = > Gatherer > .of > ( > Stack<Path>::new, > (stack, rootPath, downstream) -> > { > > stack.push(rootPath); > > while (!stack.isEmpty()) > { > > final Path path = stack.pop(); > > if (Files.isRegularFile(path)) > { > > final boolean acceptingMoreElements = > downstream.push(path); > > if (!acceptingMoreElements) > { > > return false; > > } > > } > > else > { > > try (final Stream<Path> folderContents = > Files.list(path)) > { > > folderContents.forEach(stack::push); > > } > > catch (final Exception exception) > { > > throw new IllegalStateException("Failed for " + > path, exception); > > } > > } > } > > return true; > > }, > (s1, s2) -> > { > > s1.addAll(s2); > return s1; > > }, > (stack, downstream) -> > { > > for (final Path path : stack) > { > > if (!downstream.push(path)) > { > > return; > > } > > } > > } > ) > ; > > Stream > .of(root) > .gather(gatherer) > .limit(5) > .forEach(IO::println) > ; > > } > > So, problem 2 is solved, but problem 1 is not really. Sure, I could turn my > stream parallel, but the actual meat of the processing is sequential when > it really doesn't need to be. > > Let me know if this makes more sense. And sorry, you might have to scroll > to read earlier emails in this thread to get the context. I know it was > several months back. > > > On Fri, Nov 14, 2025 at 5:42 AM Remi Forax <[email protected]> wrote: > > > Hi David, > > You can always transform an imperative code to a stream by pushing the > > element through a consumer. > > Internally, a stream uses a push iterator (see > > Spliterator.tryAdvance(consumer)). > > > > As a silly example, this is a way to write fibonacci (the recursive form) > > with a stream right in the middle. > > > > > > static void fibo(int n, IntConsumer consumer) { > > if (n < 2) { > > consumer.accept(n); > > return; > > } > > var result = Stream.of("") > > .mapMultiToInt((_, consumer2) -> { > > fibo(n - 1, consumer2); > > fibo(n - 2, consumer2); > > }) > > .sum(); > > consumer.accept(result); > > } > > > > static void main() { > > fibo(7, IO::println); > > } > > > > > > Here, I use mapMulti() to convert the imperative code to a Stream > > (there is no factory method on Stream that takes a consumer of consumer). > > > > If you also want to short-circuit, you can use a gatherer instead of > > mapMulti but short-circuiting the recursive code will require to use an > > exception as control flow (it will not be pretty). > > > > regards, > > Rémi > > > > > > ------------------------------ > > > > *From: *"David Alayachew" <[email protected]> > > *To: *"core-libs-dev" <[email protected]> > > *Sent: *Tuesday, November 11, 2025 4:36:29 AM > > *Subject: *Difficulties of recursion with Streams > > > > Hello @core-libs-dev <[email protected]>, > > > > When working with streams, I often run into situations where I have to > > "demote" back to imperative code because I am trying to solve a problem > > best solved by recursion. > > > > Consider the common use case of cycling through permutations to find all > > permutations that satisfy some condition. With recursion, the answer is > > incredibly simple -- just grab an element from the set, then call the > > recursive method with a copy of the set minus the grabbed element. Once > you > > reach the empty set, you've reached your terminal condition. > > > > Use cases like that are not only incredibly common, but usually, > > embarrassingly parallel. The example above of cycling through > permutations > > is only a few lines of imperative code, but I struggle to imagine how I > > would do this with Streams. > > > > I guess let me start by asking -- are there any good ways currently to > > accomplish the above permutation example with Streams? And if not, should > > there be? > > > > Thank you for your time and consideration. > > David Alayachew > > > > >
