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

Reply via email to