Re: RFR: 8274412: Add a method to Stream API to consume and close the stream without using try-with-resources

2021-11-06 Thread Peter Levart
On Sun, 3 Oct 2021 11:00:25 GMT, Tagir F. Valeev  wrote:

> Currently, when the stream holds a resource, it's necessary to wrap it with 
> try-with-resources. This undermines the compact and fluent style of stream 
> API calls. For example, if we want to get the `List` of files inside the 
> directory and timely close the underlying filehandle, we should use something 
> like this:
> 
> 
> List paths;
> try (Stream stream = Files.list(Path.of("/etc"))) {
> paths = stream.toList();
> }
> // use paths
> 
> 
> I suggest to add a new default method to Stream interface named 
> `consumeAndClose`, which allows performing terminal stream operation and 
> closing the stream at the same time. It may look like this:
> 
> 
> default  R consumeAndClose(Function, ? extends R> 
> function) {
> Objects.requireNonNull(function);
> try(this) {
> return function.apply(this);
> }
> }
> 
> 
> Now, it will be possible to get the list of the files in the fluent manner:
> 
> 
> List list = Files.list(Path.of("/etc")).consumeAndClose(Stream::toList);

Gosh, mailing list -> Github bridge didn't handle formatting well, so I'm 
posting this again via Github. Sorry for duplication...

This RFR is closed, but inspired by John's discussion and fueled by grief that 
I have each time when I try to combine Stream processing with resources that 
throw checked exceptions on construction and destruction (usually same kind), I 
created some helper classes/interfaces that might make such attempts easier and 
I'd like to present them here to demonstrate what is achievable with API and 
current syntax features...

Let's start with a code snippet that shows how this must be done without such 
helper classes today. A method that takes a path and a regular expression and 
returns a list of lines read from files found recursively below given path that 
contain at least one match of the regular expression:



public static List grep(Path dir, Pattern pattern) {
try (
var paths = Files.find(dir, 100, (p, a) -> true)
) {
return
paths
.filter(f -> Files.isRegularFile(f) && Files.isReadable(f))
.flatMap(
f -> {
BufferedReader br;
try {
br = Files.newBufferedReader(f, 
StandardCharsets.UTF_8);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
return br.lines().onClose(() -> {
try {
br.close();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
});
}
)
.filter(line -> pattern.matcher(line).find())
.collect(Collectors.toList());
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}



This can be rewritten in a more functional style using helpers to:




public static final Try, IOException> streamTry =
new Try<>(BaseStream::close, UncheckedIOException::new);

public static final Try ioTry =
new Try<>(Closeable::close, UncheckedIOException::new);

public static List grep(Path dir, Pattern pattern) {
return streamTry
.with(
() -> Files.find(dir, 100, (p, a) -> true)
)
.applyAndDispose(
paths -> paths
.filter(f -> Files.isRegularFile(f) && Files.isReadable(f))
.flatMap(
f -> ioTry.with(() -> Files.newBufferedReader(f, 
StandardCharsets.UTF_8))
  .apply((br, dispose) -> 
br.lines().onClose(dispose))
)
.filter(line -> pattern.matcher(line).find())
.collect(Collectors.toList())
);
}



Note that this helper is not limited to `AutoCloseable` resources. It captures 
resource destruction function and checked exception wrapping function in an 
instance of `Try` which provides two styles of handling resource lifecycle:

`with(constructor).applyAndDispose(consumptor)` - construction, consumprion 
with automatic resource disposal after the `consumptor` function terminates but 
before `applyAndDispose` terminates

`with(constructor).apply(consumptorAndDestructor)` - construction, consumption 
with arranged resource disposal by arranging to call the passed-in `Runnable` 
instance in the `consumptorAndDestructor` function itself.

Both styles are demonstrated above.

Here's the `Try` helper:

https://gist.github.com/plevart/c26e9908573d4a28c709b7218b001ea8


Regards, Peter

-


Re: RFR: 8274412: Add a method to Stream API to consume and close the stream without using try-with-resources

2021-11-06 Thread Peter Levart

Hi,

On 11/10/2021 20:42, John Rose wrote:

To summarize:  We can (and should) try to model “close-debt”
using interfaces.  Doing so opens up the usual cans of worms
with interoperability and exceptions, but still gives us a
model we can contemplate.  We can (and should) contemplate
how such a model would give us leverage for further syntax
sugar and/or combinatorial API points for handling close-debt
at various points in the language and our APIs.



This RFR is closed, but inspired by John's discussion and fueled by 
grief that I have each time when I try to combine Stream processing with 
resources that throw checked exceptions on construction and destruction 
(usually same kind), I created some helper classes/interfaces that might 
make such attempts easier and I'd like to present them here to 
demonstrate what is achievable with API and current syntax features...


Let's start with a code snippet that shows how this must be done without 
such helper classes today. A method that takes a path and a regular 
expression and returns a list of lines read from files found recursively 
below given path that contain at least one match of the regular expression:



```
    public static List grep(Path dir, Pattern pattern) {
    try (
    var paths = Files.find(dir, 100, (p, a) -> true)
    ) {
    return
    paths
    .filter(f -> Files.isRegularFile(f) && 
Files.isReadable(f))

    .flatMap(
    f -> {
    BufferedReader br;
    try {
    br = Files.newBufferedReader(f, 
StandardCharsets.UTF_8);

    } catch (IOException e) {
    throw new UncheckedIOException(e);
    }
    return br.lines().onClose(() -> {
    try {
    br.close();
    } catch (IOException e) {
    throw new UncheckedIOException(e);
    }
    });
    }
    )
    .filter(line -> pattern.matcher(line).find())
    .collect(Collectors.toList());
    } catch (IOException e) {
    throw new UncheckedIOException(e);
    }
    }
```


This can be rewritten in a more functional style using helpers to:



```
    public static final Try, IOException> streamTry =
    new Try<>(BaseStream::close, UncheckedIOException::new);

    public static final Try ioTry =
    new Try<>(Closeable::close, UncheckedIOException::new);

    public static List grep(Path dir, Pattern pattern) {
    return streamTry
    .with(
    () -> Files.find(dir, 100, (p, a) -> true)
    )
    .applyAndDispose(
    paths -> paths
    .filter(f -> Files.isRegularFile(f) && 
Files.isReadable(f))

    .flatMap(
    f -> ioTry.with(() -> 
Files.newBufferedReader(f, StandardCharsets.UTF_8))
  .apply((br, dispose) -> 
br.lines().onClose(dispose))

    )
    .filter(line -> pattern.matcher(line).find())
    .collect(Collectors.toList())
    );
    }
```


Note that this helper is not limited to `AutoCloseable` resources. It 
captures resource destruction function and checked exception wrapping 
function in an instance of `Try` which provides two styles of handling 
resource lifecycle:


`with(constructor).applyAndDispose(consumptor)` - construction, 
consumprion with automatic resource disposal after the `consumptor` 
function terminates but before `applyAndDispose` terminates


`with(constructor).apply(consumptorAndDestructor)` - construction, 
consumption with arranged resource disposal by arranging to call the 
passed-in `Runnable` instance in the `consumptorAndDestructor` function 
itself.


Both styles are demonstrated above.

Here's the `Try` helper:

https://gist.github.com/plevart/c26e9908573d4a28c709b7218b001ea8


Regards, Peter





Re: RFR: 8274412: Add a method to Stream API to consume and close the stream without using try-with-resources

2021-10-16 Thread Glavo
Oh, sorry, I found that I misunderstood the closing time. I tried it
in the wrong way, so I came to the wrong conclusion.

Thank you for your reminder.


Re: RFR: 8274412: Add a method to Stream API to consume and close the stream without using try-with-resources

2021-10-16 Thread Remi Forax



- Original Message -
> From: "Glavo" 
> To: "Tagir F.Valeev" 
> Cc: "core-libs-dev" 
> Sent: Samedi 16 Octobre 2021 06:25:40
> Subject: Re: RFR: 8274412: Add a method to Stream API to consume and close 
> the stream without using try-with-resources

> I don't think it is a perfect solution to combine the close operation
> with the terminator operation, because there are similar issues
> in the intermediate operation.
> 
> Consider this use case (for example only):
> 
> try (var stream = Files.list(path)
>.flatMap(dir -> {
>try {
>return Files.list(dir);
>} catch (IOException e) {
>return Stream.empty();
>}
>})) {
>// ...
> }
> 
> It looks closed, but it doesn't. Closing the Stream generated by flatMap
> will not close all member Stream. 

The stream is closed, from the javadoc
"Each mapped stream is closed after its contents have been placed into this 
stream."


> Further consideration is needed to
> deal with the problem of closing the Stream. Perhaps it is a withCleaner
> method that registers the Stream with the cleaner, or other solutions.
> 
> 

regards,
Rémi

> 
> 
> Tagir F.Valeev  于2021年10月4日周一 下午2:52写道:
> 
>> Currently, when the stream holds a resource, it's necessary to wrap it
>> with try-with-resources. This undermines the compact and fluent style of
>> stream API calls. For example, if we want to get the `List` of files inside
>> the directory and timely close the underlying filehandle, we should use
>> something like this:
>>
>>
>> List paths;
>> try (Stream stream = Files.list(Path.of("/etc"))) {
>> paths = stream.toList();
>> }
>> // use paths
>>
>>
>> I suggest to add a new default method to Stream interface named
>> `consumeAndClose`, which allows performing terminal stream operation and
>> closing the stream at the same time. It may look like this:
>>
>>
>> default  R consumeAndClose(Function, ? extends R>
>> function) {
>> Objects.requireNonNull(function);
>> try(this) {
>> return function.apply(this);
>> }
>> }
>>
>>
>> Now, it will be possible to get the list of the files in the fluent manner:
>>
>>
>> List list =
>> Files.list(Path.of("/etc")).consumeAndClose(Stream::toList);
>>
>> -
>>
>> Commit messages:
>>  - Fix tests
>>  - 8274412: Add a method to Stream API to consume and close the stream
>> without using try-with-resources
>>
>> Changes: https://git.openjdk.java.net/jdk/pull/5796/files
>>  Webrev: https://webrevs.openjdk.java.net/?repo=jdk=5796=00
>>   Issue: https://bugs.openjdk.java.net/browse/JDK-8274412
>>   Stats: 140 lines in 5 files changed: 135 ins; 0 del; 5 mod
>>   Patch: https://git.openjdk.java.net/jdk/pull/5796.diff
>>   Fetch: git fetch https://git.openjdk.java.net/jdk
>> pull/5796/head:pull/5796
>>
>> PR: https://git.openjdk.java.net/jdk/pull/5796


Re: RFR: 8274412: Add a method to Stream API to consume and close the stream without using try-with-resources

2021-10-15 Thread Glavo
I don't think it is a perfect solution to combine the close operation
with the terminator operation, because there are similar issues
in the intermediate operation.

Consider this use case (for example only):

try (var stream = Files.list(path)
.flatMap(dir -> {
try {
return Files.list(dir);
} catch (IOException e) {
return Stream.empty();
}
})) {
// ...
}

It looks closed, but it doesn't. Closing the Stream generated by flatMap
will not close all member Stream. Further consideration is needed to
deal with the problem of closing the Stream. Perhaps it is a withCleaner
method that registers the Stream with the cleaner, or other solutions.




Tagir F.Valeev  于2021年10月4日周一 下午2:52写道:

> Currently, when the stream holds a resource, it's necessary to wrap it
> with try-with-resources. This undermines the compact and fluent style of
> stream API calls. For example, if we want to get the `List` of files inside
> the directory and timely close the underlying filehandle, we should use
> something like this:
>
>
> List paths;
> try (Stream stream = Files.list(Path.of("/etc"))) {
> paths = stream.toList();
> }
> // use paths
>
>
> I suggest to add a new default method to Stream interface named
> `consumeAndClose`, which allows performing terminal stream operation and
> closing the stream at the same time. It may look like this:
>
>
> default  R consumeAndClose(Function, ? extends R>
> function) {
> Objects.requireNonNull(function);
> try(this) {
> return function.apply(this);
> }
> }
>
>
> Now, it will be possible to get the list of the files in the fluent manner:
>
>
> List list =
> Files.list(Path.of("/etc")).consumeAndClose(Stream::toList);
>
> -
>
> Commit messages:
>  - Fix tests
>  - 8274412: Add a method to Stream API to consume and close the stream
> without using try-with-resources
>
> Changes: https://git.openjdk.java.net/jdk/pull/5796/files
>  Webrev: https://webrevs.openjdk.java.net/?repo=jdk=5796=00
>   Issue: https://bugs.openjdk.java.net/browse/JDK-8274412
>   Stats: 140 lines in 5 files changed: 135 ins; 0 del; 5 mod
>   Patch: https://git.openjdk.java.net/jdk/pull/5796.diff
>   Fetch: git fetch https://git.openjdk.java.net/jdk
> pull/5796/head:pull/5796
>
> PR: https://git.openjdk.java.net/jdk/pull/5796
>


Re: RFR: 8274412: Add a method to Stream API to consume and close the stream without using try-with-resources

2021-10-15 Thread Tagir Valeev
Hello!

> I am not really sure we’ve gotten the right idiom here yet.  I’d like to slow 
> down a bit before making an API decision.
>
> What id suggest is capturing the proposed api and spec on list, and let’s let 
> this sit and bake for a bit longer.  My sense is that something better may 
> well emerge if we do.

I see, thanks for the discussion. Closing the PR for now.

>
> Sent from my MacBook Wheel
>
> > On Oct 9, 2021, at 5:41 AM, Tagir F.Valeev  wrote:
> >
> > On Sun, 3 Oct 2021 11:00:25 GMT, Tagir F. Valeev  
> > wrote:
> >
> >> Currently, when the stream holds a resource, it's necessary to wrap it 
> >> with try-with-resources. This undermines the compact and fluent style of 
> >> stream API calls. For example, if we want to get the `List` of files 
> >> inside the directory and timely close the underlying filehandle, we should 
> >> use something like this:
> >>
> >>
> >> List paths;
> >> try (Stream stream = Files.list(Path.of("/etc"))) {
> >>paths = stream.toList();
> >> }
> >> // use paths
> >>
> >>
> >> I suggest to add a new default method to Stream interface named 
> >> `consumeAndClose`, which allows performing terminal stream operation and 
> >> closing the stream at the same time. It may look like this:
> >>
> >>
> >>default  R consumeAndClose(Function, ? extends R> 
> >> function) {
> >>Objects.requireNonNull(function);
> >>try(this) {
> >>return function.apply(this);
> >>}
> >>}
> >>
> >>
> >> Now, it will be possible to get the list of the files in the fluent manner:
> >>
> >>
> >> List list = 
> >> Files.list(Path.of("/etc")).consumeAndClose(Stream::toList);
> >
> > CSR is [posted](https://bugs.openjdk.java.net/browse/JDK-8274994), please 
> > review!
> >
> > -
> >
> > PR: https://git.openjdk.java.net/jdk/pull/5796


Re: RFR: 8274412: Add a method to Stream API to consume and close the stream without using try-with-resources

2021-10-11 Thread Remi Forax
I agree with the idea of a try() syntax, but i don't think we need more 
interfaces.

Yes, John is right about the fact that the TWR Aucloseable does not work well 
with checked exceptions,
but the issue is more that there is nothing that works well with checked 
exceptions because Java has no way to compose them correctly
(remainder: we should remove the idea of checked exceptions from the language 
given that it's a backward compatible change and Kotlin, C# are safer than Java 
because users of those languages are not used to write a 
try/catch/printStackTrace in those languages unlike in Java).

So for me, AutoCloseable is enough.

The problem is more than a fluent API are expression oriented and a 
try-with-resources is statement oriented, hence the mismatch.

Like we have introduced the switch expression, here we need a 
try-with-resources expression.

There are good reasons to have a try-with-resources expression
1) it can be used to track a fluent chain that should close() the resources
2) the compiler can guarantee that the stream/reader/etc around the resources 
does not leak outside.

In term of syntax, i don't think that the arrow syntax (the one used for a case 
of a switch case or a lambda) should be used here, because using a block 
expression seems to be always a mistake.

I like the proposal from John, a try(expression) or a try expression (without 
the parenthesis).

regards,
Rémi


- Original Message -
> From: "John Rose" 
> To: "Paul Sandoz" , "Brian Goetz" 
> , "Tagir F.Valeev"
> 
> Cc: "core-libs-dev" 
> Sent: Lundi 11 Octobre 2021 20:42:20
> Subject: Re: RFR: 8274412: Add a method to Stream API to consume and close 
> the stream without using try-with-resources

> So the purpose of TWR is to hold an object with a “close-debt”
> (debt of a future call to close) and pay it at the end of a block,
> sort of like C++ RAII (but also sort of not).
> 
> But fluent syntaxes (which I like very much and hope to see
> more of in the future!) don’t play well with blocks, so if a
> fluent chain (any part of that chain:  It’s multiple objects)
> incurs a “close-debt”, it’s hard to jam a TWR block into it.
> 
> Hence the current proposal.  I agree with Brian and Paul
> that we haven’t examined all the corners of this problem
> yet.  And I’d like to poke at the concept of “close-debt” to
> help with the examination.
> 
> Just for brain storming, I think we could model “close-debt”
> outside either fluent API structure or TWR block structure.
> Java O-O APIs are the pre-eminent way to model things in
> Java, and they work exceedingly well, when used with skill.
> 
> AutoCloseable models close-debt of course.  But it has two
> weaknesses:  It doesn’t model anything *other* than the
> debt, and its (sole) method skates awkwardly around the
> issue of checked exceptions.  (It requires an override with
> exception type narrowing to be used in polite company.)
> AC is more of an integration hook with TWR, rather than
> a general-purpose model for close-debt.  Therefore it doesn’t
> teach us much about close-debt in a fluent setting.
> 
> Surely we can model close-debt better.  Let’s say that an
> operation (expression) with close-debt *also* has a return
> value and (for grins) *also* has an exception it might throw.
> This gets us to an API closer to Optional.  (If you hear the
> noise of a monad snuffling around in the dark outside
> your tent, you are not wrong.)
> 
> interface MustClose_1 {
>   T get() throws X;  //or could throw some Y or nothing at all
>   void close() throws X;
> }
> 
> (I wish we had an easier way to associate such an X
> with such a T, so that Stream could be more
> interoperable with simple Stream.  It’s a pain to
> carry around those X arguments.  So I’ll drop X now.)
> 
> interface MustClose_2 {
>   T get();
>   void close() throws Exception;
> }
> 
> An expression of this type requires (in general) two
> operations to finish up:  It must be closed, and the result
> (type T) must be gotten.  There’s an issue of coupling between
> the two methods; I would say, decouple their order, so that
> the “get” call, as with Optional, can be done any time,
> and the “close” call can be done in any order relative
> to “get”.  Both calls should be idempotent, I think.
> But that’s all second-order detail.
> 
> A first-order detail is the apparent but incorrect 1-1 relation
> between T values and close-debts.  That’s very wrong;
> a closable stream on 1,000 values has one close-debt,
> not 1,000.  So maybe we need:
> 
> interface MustClose_3 {
>   S map(Function value);
>   void close() throws Exception;
> }
> 
> That “map” method looks a little like Remi’s apply
> method.  Did I mention this design requires skill
> (as well as flexibility, with one hand already tied
> by checked exceptions)?  I’m at the edge of my own
> skill here, but I think there’s good ground to explore
> here.
> 
> In a fluent setting, a Stream that incurs a close-debt
> might be typed (after incurring the 

Re: RFR: 8274412: Add a method to Stream API to consume and close the stream without using try-with-resources

2021-10-11 Thread John Rose
So the purpose of TWR is to hold an object with a “close-debt”
(debt of a future call to close) and pay it at the end of a block,
sort of like C++ RAII (but also sort of not).

But fluent syntaxes (which I like very much and hope to see
more of in the future!) don’t play well with blocks, so if a
fluent chain (any part of that chain:  It’s multiple objects)
incurs a “close-debt”, it’s hard to jam a TWR block into it.

Hence the current proposal.  I agree with Brian and Paul
that we haven’t examined all the corners of this problem
yet.  And I’d like to poke at the concept of “close-debt” to
help with the examination.

Just for brain storming, I think we could model “close-debt”
outside either fluent API structure or TWR block structure.
Java O-O APIs are the pre-eminent way to model things in
Java, and they work exceedingly well, when used with skill.

AutoCloseable models close-debt of course.  But it has two
weaknesses:  It doesn’t model anything *other* than the
debt, and its (sole) method skates awkwardly around the
issue of checked exceptions.  (It requires an override with
exception type narrowing to be used in polite company.)
AC is more of an integration hook with TWR, rather than
a general-purpose model for close-debt.  Therefore it doesn’t
teach us much about close-debt in a fluent setting.

Surely we can model close-debt better.  Let’s say that an
operation (expression) with close-debt *also* has a return
value and (for grins) *also* has an exception it might throw.
This gets us to an API closer to Optional.  (If you hear the
noise of a monad snuffling around in the dark outside
your tent, you are not wrong.)

interface MustClose_1 {
   T get() throws X;  //or could throw some Y or nothing at all
   void close() throws X;
}

(I wish we had an easier way to associate such an X
with such a T, so that Stream could be more
interoperable with simple Stream.  It’s a pain to
carry around those X arguments.  So I’ll drop X now.)

interface MustClose_2 {
   T get();
   void close() throws Exception;
}

An expression of this type requires (in general) two
operations to finish up:  It must be closed, and the result
(type T) must be gotten.  There’s an issue of coupling between
the two methods; I would say, decouple their order, so that
the “get” call, as with Optional, can be done any time,
and the “close” call can be done in any order relative
to “get”.  Both calls should be idempotent, I think.
But that’s all second-order detail.

A first-order detail is the apparent but incorrect 1-1 relation
between T values and close-debts.  That’s very wrong;
a closable stream on 1,000 values has one close-debt,
not 1,000.  So maybe we need:

interface MustClose_3 {
   S map(Function value);
   void close() throws Exception;
}

That “map” method looks a little like Remi’s apply
method.  Did I mention this design requires skill
(as well as flexibility, with one hand already tied
by checked exceptions)?  I’m at the edge of my own
skill here, but I think there’s good ground to explore
here.

In a fluent setting, a Stream that incurs a close-debt
might be typed (after incurring the debt, perhaps in a
transform) as Stream>, and somehow
all consumers of the MustClose, such as map and
collect operations on the Stream, would correctly
unpack each T from the MC, and then repack
the result into the MC<.> wrapper.

var res = openAFileStream().map(…).collect(…);

Here the first method call returns a Stream with
close-debt mixed into its type.  The map and collect
calls would wire both parts:  The T values flowing
through, and the close-debt.  Who takes responsibility
for paying the close debt?  Maybe an extra call
at the end:  …map(…).collectAndClose(…).
Or maybe the stream “knows” internally that since
its type has a close debt, all of its terminal operations
have to pay off that debt as they collect the payloads.
So it would be automatic, somehow, inside of
collect, forEach, etc.

To make the parts hook up right, you might need
reified generics, or maybe an amended type
MustCloseStream <: Stream>,
like the LongStream <: Stream we dislike.
I’m only proposing as a thought exercise for now.

Maybe the MustCloseStream takes an explicit close
method which produces a regular stream over the
base type.  The explicit close method would release
resources and buffer anything necessary to produce
an in-memory Stream.  You’d want to call it
late in the fluent chain, after filtering and flat-mapping
is done, just before a collect for forEach.

Here’s a streamlined version of MustClose that
I like, which sidesteps the problem of mutual ordering
of two methods:

interface MustClose_4 {
   R getAndClose() throws Exception;
   default void close() throws Exception { getAndClose(); }
}

Here, R is not an element type of a stream, but rather
the final result (maybe Void) of some terminal operation.

Such an interface could interact with syntax, too.  For
example, it might help with TWR expressions (if we
wanted to think about that):

var res 

Re: RFR: 8274412: Add a method to Stream API to consume and close the stream without using try-with-resources

2021-10-11 Thread Paul Sandoz
Hi Tagir,

Do you mind if we slow down on this and let the idea bake somewhat, captured in 
this thread/issue/PR(draft).

I am always a little wary of compact-only or fluent-only methods, as I find 
them harder to justify. In this case I think there might be something more with 
regards to a pattern/idiom, and making it easier to not forgot to close. But, I 
am not sure we are there yet.

- we have had this “transform” idiom floating around for a while which is like 
your consumeAndClose, but without the try block.
  https://bugs.openjdk.java.net/browse/JDK-8140283
  And such a method was added to String.
There are likely other places where we could consider adding this idiom.

- I think we should explore adding the method you propose on AutoCloseable, 
that likely has more leverage, but we would need to do some careful analysis of 
code to ensure the risk to incompatibly is low.

The idioms “transform” and “transformAndClose” are I think related [*]. If we 
can nail down these I would feel much better about committing to them as 
methods on various classes.

Paul.

[*] If we ever get the notion of Haskell-like type classes in the platform I 
wonder if those would provide the hook, we could add later, that I am looking 
for so that we can corral these idioms.


> On Oct 3, 2021, at 11:51 PM, Tagir F.Valeev  wrote:
> 
> Currently, when the stream holds a resource, it's necessary to wrap it with 
> try-with-resources. This undermines the compact and fluent style of stream 
> API calls. For example, if we want to get the `List` of files inside the 
> directory and timely close the underlying filehandle, we should use something 
> like this:
> 
> 
> List paths;
> try (Stream stream = Files.list(Path.of("/etc"))) {
>paths = stream.toList();
> }
> // use paths
> 
> 
> I suggest to add a new default method to Stream interface named 
> `consumeAndClose`, which allows performing terminal stream operation and 
> closing the stream at the same time. It may look like this:
> 
> 
>default  R consumeAndClose(Function, ? extends R> 
> function) {
>Objects.requireNonNull(function);
>try(this) {
>return function.apply(this);
>}
>}
> 
> 
> Now, it will be possible to get the list of the files in the fluent manner:
> 
> 
> List list = Files.list(Path.of("/etc")).consumeAndClose(Stream::toList);
> 
> -
> 
> Commit messages:
> - Fix tests
> - 8274412: Add a method to Stream API to consume and close the stream without 
> using try-with-resources
> 
> Changes: https://git.openjdk.java.net/jdk/pull/5796/files
> Webrev: https://webrevs.openjdk.java.net/?repo=jdk=5796=00
>  Issue: https://bugs.openjdk.java.net/browse/JDK-8274412
>  Stats: 140 lines in 5 files changed: 135 ins; 0 del; 5 mod
>  Patch: https://git.openjdk.java.net/jdk/pull/5796.diff
>  Fetch: git fetch https://git.openjdk.java.net/jdk pull/5796/head:pull/5796
> 
> PR: https://git.openjdk.java.net/jdk/pull/5796



Re: RFR: 8274412: Add a method to Stream API to consume and close the stream without using try-with-resources

2021-10-10 Thread Brian Goetz
I am not really sure we’ve gotten the right idiom here yet.  I’d like to slow 
down a bit before making an API decision.  

What id suggest is capturing the proposed api and spec on list, and let’s let 
this sit and bake for a bit longer.  My sense is that something better may well 
emerge if we do. 

Sent from my MacBook Wheel

> On Oct 9, 2021, at 5:41 AM, Tagir F.Valeev  wrote:
> 
> On Sun, 3 Oct 2021 11:00:25 GMT, Tagir F. Valeev  wrote:
> 
>> Currently, when the stream holds a resource, it's necessary to wrap it with 
>> try-with-resources. This undermines the compact and fluent style of stream 
>> API calls. For example, if we want to get the `List` of files inside the 
>> directory and timely close the underlying filehandle, we should use 
>> something like this:
>> 
>> 
>> List paths;
>> try (Stream stream = Files.list(Path.of("/etc"))) {
>>paths = stream.toList();
>> }
>> // use paths
>> 
>> 
>> I suggest to add a new default method to Stream interface named 
>> `consumeAndClose`, which allows performing terminal stream operation and 
>> closing the stream at the same time. It may look like this:
>> 
>> 
>>default  R consumeAndClose(Function, ? extends R> 
>> function) {
>>Objects.requireNonNull(function);
>>try(this) {
>>return function.apply(this);
>>}
>>}
>> 
>> 
>> Now, it will be possible to get the list of the files in the fluent manner:
>> 
>> 
>> List list = 
>> Files.list(Path.of("/etc")).consumeAndClose(Stream::toList);
> 
> CSR is [posted](https://bugs.openjdk.java.net/browse/JDK-8274994), please 
> review!
> 
> -
> 
> PR: https://git.openjdk.java.net/jdk/pull/5796


Re: RFR: 8274412: Add a method to Stream API to consume and close the stream without using try-with-resources

2021-10-09 Thread Remi Forax



- Original Message -
> From: "Tagir F.Valeev" 
> To: "core-libs-dev" 
> Sent: Lundi 4 Octobre 2021 08:51:55
> Subject: RFR: 8274412: Add a method to Stream API to consume and close the 
> stream without using try-with-resources

> Currently, when the stream holds a resource, it's necessary to wrap it with
> try-with-resources. This undermines the compact and fluent style of stream API
> calls. For example, if we want to get the `List` of files inside the directory
> and timely close the underlying filehandle, we should use something like this:
> 
> 
> List paths;
> try (Stream stream = Files.list(Path.of("/etc"))) {
>paths = stream.toList();
> }
> // use paths
> 
> 
> I suggest to add a new default method to Stream interface named
> `consumeAndClose`, which allows performing terminal stream operation and
> closing the stream at the same time. It may look like this:
> 
> 
>default  R consumeAndClose(Function, ? extends R> 
> function)
>{
>Objects.requireNonNull(function);
>try(this) {
>return function.apply(this);
>}
>}
> 
> 
> Now, it will be possible to get the list of the files in the fluent manner:
> 
> 
> List list = Files.list(Path.of("/etc")).applyAndClose(Stream::toList);


I would prefer the method to be called applyAndClose() because it is what the 
method does, it applies the function and closes the stream.

There are two missing information in the javadoc
- the function taken as parameter should not return a stream, because the 
stream will be closed
  This is not okay
List list = Files.list(Path.of("/etc")).applyAndClose(s -> 
s).toList();


- if there are intermediary operations, they have to be done in the function 
taken as parameter and not before calling applyAndClose()
  This is okay
List list = Files.list(Path.of("/etc")).applyAndClose(s -> 
s.map(path -> Integer.parseInt(path.toString())).toList());

  This is not okay
List list = Files.list(Path.of("/etc")).map(path -> 
Integer.parseInt(path.toString())).applyAndClose(Stream::toList);


In both case, IDEs can help, but i think it should be written explicitly in the 
javadoc.


> 
> -
> 
> Commit messages:
> - Fix tests
> - 8274412: Add a method to Stream API to consume and close the stream without
> using try-with-resources
> 
> Changes: https://git.openjdk.java.net/jdk/pull/5796/files
> Webrev: https://webrevs.openjdk.java.net/?repo=jdk=5796=00
>  Issue: https://bugs.openjdk.java.net/browse/JDK-8274412
>  Stats: 140 lines in 5 files changed: 135 ins; 0 del; 5 mod
>  Patch: https://git.openjdk.java.net/jdk/pull/5796.diff
>  Fetch: git fetch https://git.openjdk.java.net/jdk pull/5796/head:pull/5796
> 
> PR: https://git.openjdk.java.net/jdk/pull/5796


Re: RFR: 8274412: Add a method to Stream API to consume and close the stream without using try-with-resources

2021-10-09 Thread Tagir F . Valeev
On Sun, 3 Oct 2021 11:00:25 GMT, Tagir F. Valeev  wrote:

> Currently, when the stream holds a resource, it's necessary to wrap it with 
> try-with-resources. This undermines the compact and fluent style of stream 
> API calls. For example, if we want to get the `List` of files inside the 
> directory and timely close the underlying filehandle, we should use something 
> like this:
> 
> 
> List paths;
> try (Stream stream = Files.list(Path.of("/etc"))) {
> paths = stream.toList();
> }
> // use paths
> 
> 
> I suggest to add a new default method to Stream interface named 
> `consumeAndClose`, which allows performing terminal stream operation and 
> closing the stream at the same time. It may look like this:
> 
> 
> default  R consumeAndClose(Function, ? extends R> 
> function) {
> Objects.requireNonNull(function);
> try(this) {
> return function.apply(this);
> }
> }
> 
> 
> Now, it will be possible to get the list of the files in the fluent manner:
> 
> 
> List list = Files.list(Path.of("/etc")).consumeAndClose(Stream::toList);

CSR is [posted](https://bugs.openjdk.java.net/browse/JDK-8274994), please 
review!

-

PR: https://git.openjdk.java.net/jdk/pull/5796


RFR: 8274412: Add a method to Stream API to consume and close the stream without using try-with-resources

2021-10-04 Thread Tagir F . Valeev
Currently, when the stream holds a resource, it's necessary to wrap it with 
try-with-resources. This undermines the compact and fluent style of stream API 
calls. For example, if we want to get the `List` of files inside the directory 
and timely close the underlying filehandle, we should use something like this:


List paths;
try (Stream stream = Files.list(Path.of("/etc"))) {
paths = stream.toList();
}
// use paths


I suggest to add a new default method to Stream interface named 
`consumeAndClose`, which allows performing terminal stream operation and 
closing the stream at the same time. It may look like this:


default  R consumeAndClose(Function, ? extends R> 
function) {
Objects.requireNonNull(function);
try(this) {
return function.apply(this);
}
}


Now, it will be possible to get the list of the files in the fluent manner:


List list = Files.list(Path.of("/etc")).consumeAndClose(Stream::toList);

-

Commit messages:
 - Fix tests
 - 8274412: Add a method to Stream API to consume and close the stream without 
using try-with-resources

Changes: https://git.openjdk.java.net/jdk/pull/5796/files
 Webrev: https://webrevs.openjdk.java.net/?repo=jdk=5796=00
  Issue: https://bugs.openjdk.java.net/browse/JDK-8274412
  Stats: 140 lines in 5 files changed: 135 ins; 0 del; 5 mod
  Patch: https://git.openjdk.java.net/jdk/pull/5796.diff
  Fetch: git fetch https://git.openjdk.java.net/jdk pull/5796/head:pull/5796

PR: https://git.openjdk.java.net/jdk/pull/5796