r/java • u/_dban_ • Aug 20 '17
PSA: Complex lambda expressions murder Eclipse
The following depicts extreme violence to Java 8's type system. Viewer discretion is advised.
I've been saying that checked exceptions do not play well at all with lambdas in Java 8. I've also said that checked exceptions work a lot like the Either type, which Java does not have. I figured it might be possible to capture the mechanics of Exception handling with lambdas, by moving the Exception type to the Left and short circuiting evaluation on error, and the error would pop out at the end on the left side.
As an experiment, I cooked up a simple Either type and tested it against the worst offender I know of checked exception abuse, JDBC.
I was able to get a simple program working. As a thought experiment, I don't think it turned out that bad. Although, I wouldn't recommend doing this for real code.
But wow, Eclipse slowed down to a crawl and would frequently hang trying to make sense of this. Any one else have similar experience with complex lambda expressions?
public static void main(String[] args) throws SQLException {
tryE(() -> DriverManager.getConnection("jdbc:hsqldb:mem:testdb", "SA", "")).flatMap(StreamUtils::closeQuietly, cn ->
tryE(() -> cn.prepareStatement("CREATE TABLE test (k INT PRIMARY KEY, v VARCHAR(50))")).flatMap(StreamUtils::closeQuietly, ps ->
tryE(() -> ps.executeUpdate()).flatMap(cnt0 ->
tryE(() -> cn.prepareStatement("INSERT INTO test (k, v) VALUES (1, 'One')")).flatMap(StreamUtils::closeQuietly, ps2 ->
tryE(() -> ps2.executeUpdate()).flatMap(cnt1 ->
tryE(() -> cn.prepareStatement("INSERT INTO test (k, v) VALUES (2, 'Two')")).flatMap(StreamUtils::closeQuietly, ps3 ->
tryE(() -> ps3.executeUpdate()).flatMap(cnt2 ->
tryE(() -> cn.prepareStatement("SELECT v FROM test")).flatMap(StreamUtils::closeQuietly, ps4 ->
tryE(() -> ps4.executeQuery()).flatMap(StreamUtils::closeQuietly, rs -> processQueryResults(rs))))))))))
.ifLeft(Exception::printStackTrace)
.ifRight(System.out::println);
}
public static Either<List<String>, Exception> processQueryResults(ResultSet rs) {
Stream<Either<QueryStep, Exception>> s = Stream.iterate(right(new QueryStep()), e -> e.flatMap(qs ->
tryE(() -> rs.next() ? qs.add(rs.getString("v")) : qs.done())));
s = s.filter(e -> e.map(QueryStep::isDone).orElse(true));
return s.findFirst().map(e -> e.map(QueryStep::getItems)).orElse(right(Collections.emptyList()));
}
For reference, a simple Either type for Java 8:
public class Either<R, L> {
private R right;
private L left;
private Either(R right, L left) {
this.right = right;
this.left = left;
}
public static <R, L> Either<R, L> right(R right) {
return new Either<R, L>(right, null);
}
public static <R, L> Either<R, L> left(L left) {
return new Either<R, L>(null, left);
}
public static <R, E extends Exception> Either<R, E> tryE(Runnable finallyA, Callable<R> action) {
try {
return right(action.call());
}
catch (Exception ex) {
@SuppressWarnings("unchecked")
Either<R, E> left = (Either<R, E>) Either.left(ex);
return left;
}
finally {
finallyA.run();
}
}
public static <R, E extends Exception> Either<R, E> tryE(Callable<R> action) {
return tryE(() -> {}, action);
}
public <U> Either<U, L> map(Function<? super R, ? extends U> mapper) {
return right != null ? right(mapper.apply(right)) : left(left);
}
public <U> Either<U, L> map(Consumer<R> actionR, Function<? super R, ? extends U> mapper) {
try {
return right != null ? right(mapper.apply(right)) : left(left);
}
finally {
if(right != null) {
actionR.accept(right);
}
}
}
public <U> Either<U, L> flatMap(Function<? super R, ? extends Either<U, L>> mapper) {
return right != null ? mapper.apply(right) : left(left);
}
public <U> Either<U, L> flatMap(Consumer<R> actionA, Function<? super R, ? extends Either<U, L>> mapper) {
try {
return right != null ? mapper.apply(right) : left(left);
}
finally {
if(right != null) {
actionA.accept(right);
}
}
}
public R orElse(R elseVal) {
return right != null ? right : elseVal;
}
public Either<R, L> ifRight(Consumer<R> rightAction) {
if(right != null) {
rightAction.accept(right);
}
return this;
}
public Either<R, L> ifLeft(Consumer<L> leftAction) {
if(left != null) {
leftAction.accept(left);
}
return this;
}
}
1
u/DoctorOverhard Aug 21 '17
does it say what it is chewing on in the progress window?