|
1 | 1 | package com.jnape.palatable.lambda.io;
|
2 | 2 |
|
3 | 3 | import com.jnape.palatable.lambda.adt.Either;
|
| 4 | +import com.jnape.palatable.lambda.adt.Try; |
4 | 5 | import com.jnape.palatable.lambda.adt.Unit;
|
5 | 6 | import com.jnape.palatable.lambda.adt.choice.Choice2;
|
6 | 7 | import com.jnape.palatable.lambda.functions.Fn0;
|
|
15 | 16 | import java.util.concurrent.CompletableFuture;
|
16 | 17 | import java.util.concurrent.Executor;
|
17 | 18 |
|
| 19 | +import static com.jnape.palatable.lambda.adt.Try.failure; |
18 | 20 | import static com.jnape.palatable.lambda.adt.Try.trying;
|
19 | 21 | import static com.jnape.palatable.lambda.adt.Unit.UNIT;
|
20 | 22 | import static com.jnape.palatable.lambda.adt.choice.Choice2.a;
|
@@ -81,43 +83,48 @@ public final CompletableFuture<A> unsafePerformAsyncIO() {
|
81 | 83 | * @return the guarded {@link IO}
|
82 | 84 | */
|
83 | 85 | public final IO<A> exceptionally(Fn1<? super Throwable, ? extends A> recoveryFn) {
|
| 86 | + return exceptionallyIO(t -> io(recoveryFn.apply(t))); |
| 87 | + } |
| 88 | + |
| 89 | + /** |
| 90 | + * Like {@link IO#exceptionally(Fn1) exceptionally}, but recover the {@link Throwable} via another {@link IO} |
| 91 | + * operation. If both {@link IO IOs} throw, the "cleanup" {@link IO IO's} {@link Throwable} is |
| 92 | + * {@link Throwable#addSuppressed(Throwable) suppressed} by this {@link IO IO's} {@link Throwable}. |
| 93 | + * |
| 94 | + * @param recoveryFn the recovery function |
| 95 | + * @return the guarded {@link IO} |
| 96 | + */ |
| 97 | + public final IO<A> exceptionallyIO(Fn1<? super Throwable, ? extends IO<A>> recoveryFn) { |
84 | 98 | return new IO<A>() {
|
85 | 99 | @Override
|
86 | 100 | public A unsafePerformIO() {
|
87 |
| - return trying(IO.this::unsafePerformIO).recover(recoveryFn); |
| 101 | + return trying(IO.this::unsafePerformIO) |
| 102 | + .recover(t -> trying(recoveryFn.apply(t)::unsafePerformIO) |
| 103 | + .fmap(Try::success) |
| 104 | + .recover(t2 -> { |
| 105 | + t.addSuppressed(t2); |
| 106 | + return failure(t); |
| 107 | + }) |
| 108 | + .orThrow()); |
88 | 109 | }
|
89 | 110 |
|
90 | 111 | @Override
|
91 | 112 | public CompletableFuture<A> unsafePerformAsyncIO(Executor executor) {
|
92 |
| - return IO.this.unsafePerformAsyncIO(executor).exceptionally(recoveryFn::apply); |
| 113 | + return IO.this.unsafePerformAsyncIO(executor) |
| 114 | + .thenApply(CompletableFuture::completedFuture) |
| 115 | + .exceptionally(t -> recoveryFn.apply(t).unsafePerformAsyncIO(executor) |
| 116 | + .thenApply(CompletableFuture::completedFuture) |
| 117 | + .exceptionally(t2 -> { |
| 118 | + t.addSuppressed(t2); |
| 119 | + return new CompletableFuture<A>() {{ |
| 120 | + completeExceptionally(t); |
| 121 | + }}; |
| 122 | + }).thenCompose(f -> f)) |
| 123 | + .thenCompose(f -> f); |
93 | 124 | }
|
94 | 125 | };
|
95 | 126 | }
|
96 | 127 |
|
97 |
| -// /** |
98 |
| -// * Given a function from any {@link Throwable} to the result type <code>A</code>, if this {@link IO} successfully |
99 |
| -// * yields a result, return it; otherwise, map the {@link Throwable} to the result type and return that. |
100 |
| -// * |
101 |
| -// * @param recoveryFn the recovery function |
102 |
| -// * @return the guarded {@link IO} |
103 |
| -// */ |
104 |
| -// public final IO<A> exceptionallyIO(Fn1<? super Throwable, ? extends IO<A>> recoveryFn) { |
105 |
| -// return new IO<A>() { |
106 |
| -// @Override |
107 |
| -// public A unsafePerformIO() { |
108 |
| -// return trying(IO.this::unsafePerformIO).recover(t -> recoveryFn.apply(t).unsafePerformIO()) |
109 |
| -// } |
110 |
| -// |
111 |
| -// @Override |
112 |
| -// public CompletableFuture<A> unsafePerformAsyncIO(Executor executor) { |
113 |
| -//// IO.this.unsafePerformAsyncIO(executor) |
114 |
| -//// .thenCombine() |
115 |
| -//// .exceptionally(t -> recoveryFn.apply(t).unsafePerformAsyncIO(executor)) |
116 |
| -// } |
117 |
| -// }; |
118 |
| -// } |
119 |
| - |
120 |
| - |
121 | 128 | /**
|
122 | 129 | * Return an {@link IO} that will run <code>ensureIO</code> strictly after running this {@link IO} regardless of
|
123 | 130 | * whether this {@link IO} terminates normally, analogous to a finally block.
|
|
0 commit comments