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<String> 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<BaseStream<?, ?>, IOException> streamTry =
        new Try<>(BaseStream::close, UncheckedIOException::new);

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

    public static List<String> 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



Reply via email to