lambdista/try

try-with-resource equivalent

Closed this issue · 3 comments

I think this implementation of the Try idiom is quite handy. However, I somehow miss an equivalent to the try-with-resource statement. I hacked some code and would think of something similar to this:

import java.util.function.Function;

import com.lambdista.util.Try;

public class TryWithResource {
    public static <T extends AutoCloseable,R> Function<T, Try<R>> lift(Function<T, R> consumer) {
        return (closeable) -> {
            return Try.apply(() -> {
                try (T in = closeable) {
                    return consumer.apply(in);
                }
            });
        };
    }
}

Some Unit tests:

import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;

import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;

import org.junit.Before;
import org.junit.Test;

public class TryWithResourceTest {

    private Closeable closeableMock;
    private InputStream inputStreamMock;

    @Before
    public void setup(){
        this.closeableMock = mock(Closeable.class);
        this.inputStreamMock = mock(InputStream.class);
    }

    @Test
    public void closeAbleShouldBeClosedAfterConsumption() throws IOException{
        verify(closeableMock, never()).close();
        final String result = TryWithResource.lift(closeable -> "String").apply(closeableMock).get();
        verify(closeableMock).close();
        assertThat(result, is(equalTo("String")));
    }

    @Test
    public void inputStreamShouldBeClosedAfterConsumption() throws IOException{
        verify(inputStreamMock, never()).close();
        final String result = TryWithResource.lift(inputStream -> "String").apply(inputStreamMock).get();
        verify(inputStreamMock).close();
        assertThat(result, is(equalTo("String")));
    }

    @Test
    public void inputStreamShouldBeClosedAfterException() throws IOException{
        verify(inputStreamMock, never()).close();
        TryWithResource.lift(inputStream -> {throw new RuntimeException();}).apply(inputStreamMock);
        verify(inputStreamMock).close();
    }
}

Would you consider to add such functionality?

Hi @gtrefs I'll be more than happy to add such functionality. Open a PR.

However, if you didn't mind, I'd ask you some changes:

  1. Change the statement lambda into an expression lambda
  2. Change the name from lift to create (or whatever you prefer) because I think lift would be more appropriate if it turned a function from A -> B to a function from Try<A> -> Try<B> (in general from A -> B to F[A] -> F[B]).

The result of applying 1 and 2 would be something like the following:

public class TryWithResource {
    public static <T extends AutoCloseable, R> Function<T, Try<R>> create(Function<T, R> consumer) {
        return closeable -> Try.apply(() -> {
            try (T in = closeable) {
                return consumer.apply(in);
            }
        });
    }
}

Would you mind writing, besides the test, a simple usage example under the example package?

Thank you so much for your contribution.

Regards

Nice :). Sure, I can integrate your comments into the code. I know that in Scala there are companion objects which have an apply method. Thus, you can use the Types in a more funcrional way. However, in Java there is nothing like this. If you don't mind, I would add the static method to the Try class directly.

public class Try<T> {
  // ...
  public static <T extends AutoCloseable, R> Function<T, Try<R>> applyWithResource(Function<T, R> consumer) {
        return closeable -> Try.apply(() -> {
            try (T in = closeable) {
                return consumer.apply(in);
            }
        });
    }
}

Your Opinion?

That's OK for me. Thank you!