summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBen Culkin <scorpress@gmail.com>2021-02-26 16:15:54 -0500
committerBen Culkin <scorpress@gmail.com>2021-02-26 16:15:54 -0500
commita7352711e1d6c36b16235c042e139946ad6fb35c (patch)
tree914d2232b2309af3d1468fd56e15e741e3080f44 /src
parent7783bf18664f45c76fe4e5f583dac0803087de95 (diff)
Update
Diffstat (limited to 'src')
-rw-r--r--src/main/java/bjc/data/Option.java19
-rw-r--r--src/main/java/bjc/functypes/ThrowFunction.java97
-rw-r--r--src/test/java/bjc/data/EitherTest.java40
3 files changed, 104 insertions, 52 deletions
diff --git a/src/main/java/bjc/data/Option.java b/src/main/java/bjc/data/Option.java
index 6c76d59..6ca8b13 100644
--- a/src/main/java/bjc/data/Option.java
+++ b/src/main/java/bjc/data/Option.java
@@ -13,7 +13,15 @@ import java.util.function.UnaryOperator;
*/
public class Option<ContainedType> implements Holder<ContainedType> {
private ContainedType held;
-
+ private boolean isHolding;
+
+ /**
+ * Create a new empty optional.
+ */
+ public Option() {
+ isHolding = false;
+ }
+
/**
* Create a new optional, using the given initial value.
*
@@ -22,12 +30,13 @@ public class Option<ContainedType> implements Holder<ContainedType> {
*/
public Option(final ContainedType seed) {
held = seed;
+ isHolding = true;
}
@Override
public <BoundType> Holder<BoundType>
bind(final Function<ContainedType, Holder<BoundType>> binder) {
- if (held == null) return new Option<>(null);
+ if (isHolding) return new Option<>();
return binder.apply(held);
}
@@ -41,7 +50,7 @@ public class Option<ContainedType> implements Holder<ContainedType> {
@Override
public <MappedType> Holder<MappedType>
map(final Function<ContainedType, MappedType> mapper) {
- if (held == null) return new Option<>(null);
+ if (isHolding) return new Option<>();
return new Option<>(mapper.apply(held));
}
@@ -49,7 +58,7 @@ public class Option<ContainedType> implements Holder<ContainedType> {
@Override
public Holder<ContainedType>
transform(final UnaryOperator<ContainedType> transformer) {
- if (held != null) held = transformer.apply(held);
+ if (isHolding) held = transformer.apply(held);
return this;
}
@@ -57,7 +66,7 @@ public class Option<ContainedType> implements Holder<ContainedType> {
@Override
public <UnwrappedType> UnwrappedType
unwrap(final Function<ContainedType, UnwrappedType> unwrapper) {
- if (held == null) return null;
+ if (isHolding) return null;
return unwrapper.apply(held);
}
diff --git a/src/main/java/bjc/functypes/ThrowFunction.java b/src/main/java/bjc/functypes/ThrowFunction.java
index 132f55c..3714cad 100644
--- a/src/main/java/bjc/functypes/ThrowFunction.java
+++ b/src/main/java/bjc/functypes/ThrowFunction.java
@@ -1,5 +1,6 @@
package bjc.functypes;
+import java.util.*;
import java.util.function.*;
/**
@@ -25,6 +26,46 @@ public interface ThrowFunction<InputType, ReturnType, ExType extends Throwable>
public ReturnType apply(InputType arg) throws ExType;
/**
+ * Converts this into a {@link Function} by handling any thrown exceptions,
+ * then mapping the return type to get a consistent return type.
+ *
+ * @param <NewReturn> The new return type.
+ *
+ * @param clasz The class of the handled exception. This needs to be provided
+ * because you can't catch a generic exception, and we want to
+ * make sure that we aren't catching any types of exception that
+ * we aren't supposed to.
+ * @param mapper The function which maps the normal return.
+ * @param handler The handler to use.
+ *
+ * @return A function which will either return its proper value, or the result
+ * of invoking the handler.
+ */
+ @SuppressWarnings("unchecked")
+ default <NewReturn> Function<InputType, NewReturn> swallowMap(
+ Class<ExType> clasz,
+ Function<ReturnType, NewReturn> mapper,
+ Function<ExType, NewReturn> handler)
+ {
+ return (inp) -> {
+ try {
+ return mapper.apply(this.apply(inp));
+ } catch (Throwable ex) {
+ if (clasz.isInstance(ex)) {
+ // Swallow this
+ return handler.apply((ExType) ex);
+ } else {
+ String msg = "Exception of incorrect type to be handled, only "
+ + clasz.getName()
+ + " are handled";
+
+ throw new RuntimeException(msg, ex);
+ }
+ }
+ };
+ }
+
+ /**
* Converts this into a {@link Function} by handling any thrown exceptions.
*
* @param clasz The class of the handled exception. This needs to be provided
@@ -37,30 +78,10 @@ public interface ThrowFunction<InputType, ReturnType, ExType extends Throwable>
* @return A function which will either return its proper value, or the result
* of invoking the handler.
*/
- @SuppressWarnings("unchecked")
default Function<InputType, ReturnType> swallow(
Class<ExType> clasz, Function<ExType, ReturnType> handler)
{
- return (inp) ->
- {
- try
- {
- return this.apply(inp);
- } catch (Throwable ex) {
- if (clasz.isInstance(ex))
- {
- // Swallow this
- return handler.apply((ExType) ex);
- } else
- {
- String msg = "Exception of incorrect type to be handled, only "
- + clasz.getName()
- + " are handled";
-
- throw new RuntimeException(msg, ex);
- }
- }
- };
+ return swallowMap(clasz, (arg) -> arg, handler);
}
/**
@@ -73,20 +94,16 @@ public interface ThrowFunction<InputType, ReturnType, ExType extends Throwable>
*/
@SuppressWarnings("unchecked")
default Function<InputType, ReturnType> recover(
- Class<ExType> clasz, BiFunction<InputType, ExType, InputType> rescue) {
- return Fixpoints.fix((arg, self) ->
- {
- try
- {
+ Class<ExType> clasz, BiFunction<InputType, ExType, InputType> rescue)
+ {
+ return Fixpoints.fix((arg, self) -> {
+ try {
return this.apply(arg);
- } catch (Throwable ex)
- {
- if (clasz.isInstance(ex))
- {
+ } catch (Throwable ex) {
+ if (clasz.isInstance(ex)) {
// Swallow this
return self.apply(rescue.apply(arg, (ExType) ex));
- } else
- {
+ } else {
String msg = "Exception of incorrect type to be handled, only "
+ clasz.getName()
+ " are handled";
@@ -125,6 +142,22 @@ public interface ThrowFunction<InputType, ReturnType, ExType extends Throwable>
return (arg) -> func.apply(this.apply(arg));
}
+ /** Convert this function into one which will return an empty optional if an
+ * exception is thrown, returning an optional containing the value otherwise.
+ *
+ * Note that if this function returns a null value by itself, that will also
+ * yield an empty nullable.
+ *
+ * @param clasz The class of the exception. Needed because of type erasure,
+ * to ensure that we are catching the proper class.
+ *
+ * @return A function which returns an optional instead.
+ */
+ default Function<InputType, Optional<ReturnType>>
+ makeTotal(Class<ExType> clasz)
+ {
+ return swallowMap(clasz, Optional::ofNullable, (ignored) -> Optional.empty());
+ }
/**
* ThrowFunctions and functions which return a {@link Thrower} are isomorphic.
*
diff --git a/src/test/java/bjc/data/EitherTest.java b/src/test/java/bjc/data/EitherTest.java
index bbf94b7..ef2d12b 100644
--- a/src/test/java/bjc/data/EitherTest.java
+++ b/src/test/java/bjc/data/EitherTest.java
@@ -2,24 +2,34 @@ package bjc.data;
import static org.junit.Assert.*;
-import org.junit.Test;
+import java.util.*;
-/**
- * Test Either
- * @author Ben Culkin
- *
- */
-public class EitherTest {
+import org.junit.*;
- /**
- * Do a test of Either.
- */
+@SuppressWarnings("javadoc")
+public class EitherTest
+{
+ private Either<String, String> leftEither;
+ private Either<String, String> rightEither;
+
+ @Before
+ public void setUp() throws Exception {
+ leftEither = Either.left("left");
+ rightEither = Either.right("right");
+ }
+
+ @Test
+ public void testIsLeft() {
+ assertTrue("isLeft properly marks left eithers", leftEither.isLeft());
+ assertFalse("isLeft properly marks right eithers", rightEither.isLeft());
+ }
+
@Test
- public void test() {
- Either<String, String> left = Either.left("left");
- Either<String, String> right = Either.right("right");
-
- assertNotEquals(left, right);
+ public void testGetLeft() {
+ assertEquals("getLeft treats left eithers properly",
+ Optional.of("left"), leftEither.getLeft());
+ assertEquals("getLeft treats right eithers properly",
+ Optional.empty(), rightEither.getLeft());
}
}