summaryrefslogtreecommitdiff
path: root/src/main/java/bjc
diff options
context:
space:
mode:
authorBen Culkin <scorpress@gmail.com>2022-09-24 12:34:30 -0400
committerBen Culkin <scorpress@gmail.com>2022-09-24 12:34:30 -0400
commitf7d96d88798c904f69f0b5306a5ddecd0456b377 (patch)
tree5a41a891a629322f19d649c938a92ac293b19c0e /src/main/java/bjc
parente3501bcc50d6579dd16ce678436fa93d6e528e5f (diff)
Add some additional combinators, of various sorts
Diffstat (limited to 'src/main/java/bjc')
-rw-r--r--src/main/java/bjc/data/Either.java144
-rw-r--r--src/main/java/bjc/functypes/CombinatorBirds.java212
-rw-r--r--src/main/java/bjc/functypes/Combinators.java231
-rw-r--r--src/main/java/bjc/functypes/ElseFunction.java68
-rw-r--r--src/main/java/bjc/functypes/ForEach.java115
-rw-r--r--src/main/java/bjc/functypes/TriFunction.java91
-rw-r--r--src/main/java/bjc/functypes/Unit.java17
7 files changed, 688 insertions, 190 deletions
diff --git a/src/main/java/bjc/data/Either.java b/src/main/java/bjc/data/Either.java
index 75447f2..90efba7 100644
--- a/src/main/java/bjc/data/Either.java
+++ b/src/main/java/bjc/data/Either.java
@@ -3,54 +3,46 @@ package bjc.data;
import java.util.*;
import java.util.function.*;
+import bjc.functypes.ID;
+
/**
* Represents a pair where only one side has a value.
*
* @author ben
*
- * @param <LeftType>
- * The type that could be on the left.
+ * @param <LeftType> The type that could be on the left.
*
- * @param <RightType>
- * The type that could be on the right.
+ * @param <RightType> The type that could be on the right.
*
*/
public class Either<LeftType, RightType> {
/**
* Create a new either with the left value occupied.
*
- * @param <LeftType>
- * The type of the left value.
+ * @param <LeftType> The type of the left value.
*
- * @param <RightType>
- * The type of the empty right value.
+ * @param <RightType> The type of the empty right value.
*
- * @param left
- * The value to put on the left.
+ * @param left The value to put on the left.
*
* @return An either with the left side occupied.
*/
- public static <LeftType, RightType> Either<LeftType, RightType>
- left(final LeftType left) {
+ public static <LeftType, RightType> Either<LeftType, RightType> left(final LeftType left) {
return new Either<>(left, null);
}
/**
* Create a new either with the right value occupied.
*
- * @param <LeftType>
- * The type of the empty left value.
+ * @param <LeftType> The type of the empty left value.
*
- * @param <RightType>
- * The type of the right value.
+ * @param <RightType> The type of the right value.
*
- * @param right
- * The value to put on the right.
+ * @param right The value to put on the right.
*
* @return An either with the right side occupied.
*/
- public static <LeftType, RightType> Either<LeftType, RightType>
- right(final RightType right) {
+ public static <LeftType, RightType> Either<LeftType, RightType> right(final RightType right) {
return new Either<>(null, right);
}
@@ -75,56 +67,50 @@ public class Either<LeftType, RightType> {
/**
* Perform a mapping over this either.
*
- * @param <NewLeft> The new left type.
+ * @param <NewLeft> The new left type.
* @param <NewRight> The new right type.
*
- * @param leftFunc The function to apply if this is a left either.
- * @param rightFunc The function to apply if this is a right either.
+ * @param leftFunc The function to apply if this is a left either.
+ * @param rightFunc The function to apply if this is a right either.
*
- * @return A new either, containing a value transformed by the appropriate function.
+ * @return A new either, containing a value transformed by the appropriate
+ * function.
*/
- public <NewLeft, NewRight> Either<NewLeft, NewRight> map(
- Function<LeftType, NewLeft> leftFunc,
- Function<RightType, NewRight> rightFunc)
- {
- if (isLeft) return left(leftFunc.apply(leftVal));
- else return right(rightFunc.apply(rightVal));
+ public <NewLeft, NewRight> Either<NewLeft, NewRight> map(Function<LeftType, NewLeft> leftFunc,
+ Function<RightType, NewRight> rightFunc) {
+ return isLeft ? left(leftFunc.apply(leftVal)) : right(rightFunc.apply(rightVal));
}
-
+
/**
* Extract the value from this Either.
*
- * @param <Common> The common type to extract.
+ * @param <Common> The common type to extract.
*
- * @param leftHandler The function to handle left-values.
+ * @param leftHandler The function to handle left-values.
* @param rightHandler The function to handle right-values.
*
* @return The result of applying the proper function.
*/
- public <Common> Common extract(
- Function<LeftType, Common> leftHandler,
- Function<RightType, Common> rightHandler)
- {
- if (isLeft) return leftHandler.apply(leftVal);
- else return rightHandler.apply(rightVal);
+ public <Common> Common extract(Function<LeftType, Common> leftHandler, Function<RightType, Common> rightHandler) {
+ return isLeft ? leftHandler.apply(leftVal) : rightHandler.apply(rightVal);
}
-
+
/**
* Perform an action on this either.
*
- * @param leftHandler The handler of left values.
+ * @param leftHandler The handler of left values.
* @param rightHandler The handler of right values.
*/
- public void pick(
- Consumer<LeftType> leftHandler, Consumer<RightType> rightHandler)
- {
- if (isLeft) leftHandler.accept(leftVal);
- else rightHandler.accept(rightVal);
+ public void pick(Consumer<LeftType> leftHandler, Consumer<RightType> rightHandler) {
+ if (isLeft)
+ leftHandler.accept(leftVal);
+ else
+ rightHandler.accept(rightVal);
}
/**
- * Check if this either is left-aligned (has the left value filled,
- * not the right value).
+ * Check if this either is left-aligned (has the left value filled, not the
+ * right value).
*
* @return Whether this either is left-aligned.
*/
@@ -140,23 +126,21 @@ public class Either<LeftType, RightType> {
public Optional<LeftType> getLeft() {
return Optional.ofNullable(leftVal);
}
-
+
/**
- * Get the left value of this either, or get a {@link NoSuchElementException}
- * if there isn't one.
+ * Get the left value of this either, or get a {@link NoSuchElementException} if
+ * there isn't one.
*
* @return The left value of this either.
*
* @throws NoSuchElementException If this either doesn't have a left value.
*/
public LeftType forceLeft() {
- if (isLeft)
- {
+ if (isLeft) {
return leftVal;
- } else
- {
- throw new NoSuchElementException("Either has no left value, is right value");
}
+
+ throw new NoSuchElementException("Either has no left value, is right value");
}
/**
@@ -167,7 +151,7 @@ public class Either<LeftType, RightType> {
public Optional<RightType> getRight() {
return Optional.ofNullable(rightVal);
}
-
+
/**
* Get the right value of this either, or get a {@link NoSuchElementException}
* if there isn't one.
@@ -177,17 +161,32 @@ public class Either<LeftType, RightType> {
* @throws NoSuchElementException If this either doesn't have a right value.
*/
public RightType forceRight() {
- if (isLeft)
- {
+ if (isLeft) {
throw new NoSuchElementException("Either has no right value, has left value");
- } else
- {
- return rightVal;
}
+
+ return rightVal;
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T> Either<LeftType, T> newRight() {
+ if (isLeft) return (Either<LeftType, T>) this;
+
+ throw new NoSuchElementException("Can't replace right type on right Either");
}
- // Misc. overrides
+ @SuppressWarnings("unchecked")
+ public <T> Either<T, RightType> newLeft() {
+ if (isLeft)
+ throw new NoSuchElementException("Can't replace left type on left Either");
+ return (Either<T, RightType>) this;
+ }
+ public static <T> T collapse(Either<T, T> eth) {
+ return eth.extract(ID.id(), ID.id());
+ }
+ // Misc. overrides
+
@Override
public int hashCode() {
return Objects.hash(isLeft, leftVal, rightVal);
@@ -195,20 +194,21 @@ public class Either<LeftType, RightType> {
@Override
public boolean equals(Object obj) {
- if (this == obj) return true;
- if (obj == null) return false;
- if (getClass() != obj.getClass()) return false;
-
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+
Either<?, ?> other = (Either<?, ?>) obj;
-
- return isLeft == other.isLeft
- && Objects.equals(leftVal, other.leftVal)
+
+ return isLeft == other.isLeft && Objects.equals(leftVal, other.leftVal)
&& Objects.equals(rightVal, other.rightVal);
}
@Override
public String toString() {
- return String.format("Either [leftVal='%s', rightVal='%s', isLeft=%s]", leftVal,
- rightVal, isLeft);
+ return String.format("Either [leftVal='%s', rightVal='%s', isLeft=%s]", leftVal, rightVal, isLeft);
}
}
diff --git a/src/main/java/bjc/functypes/CombinatorBirds.java b/src/main/java/bjc/functypes/CombinatorBirds.java
new file mode 100644
index 0000000..d939a5b
--- /dev/null
+++ b/src/main/java/bjc/functypes/CombinatorBirds.java
@@ -0,0 +1,212 @@
+package bjc.functypes;
+
+import java.util.function.*;
+
+/**
+ * Assorted functional combinators, named after birds as in To Mock a
+ * Mockingbird
+ *
+ * Consult <a href="https://blog.lahteenmaki.net/combinator-birds.html">this
+ * site</a> for more info
+ *
+ * @author bjcul
+ *
+ */
+@SuppressWarnings("javadoc")
+public class CombinatorBirds {
+ // Note that a good number of these birds can be composed from others, but I've
+ // generally written them in the fully explicit style
+
+ /**
+ * The 'idiot' or identity bird.
+ *
+ * @param <A> A type
+ *
+ * @return A function that returns its argument
+ */
+ public static <A> UnaryOperator<A> idiot() {
+ return (a) -> a;
+ }
+
+ public static <A, B> Function<A, B> idiotOnceRemoved(Function<A, B> func) {
+ return func;
+ }
+
+ // should variants be written that take BiFunction or a TriFunction?
+ // They'd be simple to write, just a bit of a nuisance
+ public static <A, B, C> Function<A, Function<B, C>> idiotTwiceRemoved(Function<A, Function<B, C>> func) {
+ return func;
+ }
+
+ // 'true' in lambda calculus
+ public static <A, B> Function<B, A> kestrel(A val) {
+ return (vl) -> val;
+ }
+
+ // 'false' in lambda calculus
+ public static <A, B> Function<B, B> kite(A val) {
+ return (vl) -> vl;
+ }
+
+ public static <A, B, C> Function<A, C> bluebird(Function<A, B> left, Function<B, C> right) {
+ return (vl) -> right.apply(left.apply(vl));
+ }
+
+ public static <A, B, C, D> Function<A, Function<B, D>> blackbird(Function<C, D> left,
+ Function<A, Function<B, C>> right) {
+ return (first) -> (second) -> left.apply(right.apply(first).apply(second));
+ }
+
+ public static <A, B, C, D, E> Function<A, Function<B, Function<C, E>>> bunting(Function<D, E> left,
+ Function<A, Function<B, Function<C, D>>> right) {
+ return (first) -> (second) -> (third) -> left.apply(right.apply(first).apply(second).apply(third));
+ }
+
+ public static <A, B, C, D> Function<A, D> becard(Function<C, D> f, Function<B, C> g, Function<A, B> h) {
+ return (vl) -> f.apply(g.apply(h.apply(vl)));
+ }
+
+ public static <A, B, C> Function<A, C> starling(Function<A, Function<B, C>> f, Function<A, B> g) {
+ return (vl) -> f.apply(vl).apply(g.apply(vl));
+ }
+
+ public static <A, B, C> Function<A, C> missingBird(Function<B, Function<A, C>> f, Function<A, B> g) {
+ return (vl) -> f.apply(g.apply(vl)).apply(vl);
+ }
+
+ public static <A, B, C, D> Function<A, D> phoenix(Function<B, Function<C, D>> f, Function<A, B> g,
+ Function<A, C> h) {
+ return (vl) -> f.apply(g.apply(vl)).apply(h.apply(vl));
+ }
+
+ public static <A, B, C, D> Function<B, D> dove(Function<A, Function<C, D>> f, A x, Function<B, C> g) {
+ return (vl) -> f.apply(x).apply(g.apply(vl));
+ }
+
+ public static <A, B, C, D> Function<A, Function<B, D>> cardinalPrime(Function<C, Function<A, D>> f,
+ Function<B, C> g) {
+ return (x) -> (y) -> f.apply(g.apply(y)).apply(x);
+ }
+
+ public static <A, B, C, D, E> Function<B, Function<C, E>> eagle(Function<A, Function<D, E>> f, A x,
+ Function<B, Function<C, D>> g) {
+ return (y) -> (z) -> f.apply(x).apply(g.apply(y).apply(z));
+ }
+
+ public static <A, B, C, D, E> Function<C, E> dickcissel(Function<A, Function<B, Function<D, E>>> f, A x, B y,
+ Function<C, D> g) {
+ return (z) -> f.apply(x).apply(y).apply(g.apply(z));
+ }
+
+ public static <A, B, C> Function<A, Function<A, C>> psi(Function<B, Function<B, C>> f, Function<A, B> g) {
+ return (x1) -> (x2) -> f.apply(g.apply(x1)).apply(g.apply(x2));
+ }
+
+ public static <A, B, C, D, E> Function<B, E> dovekie(Function<C, Function<D, E>> f, Function<A, C> g, A x,
+ Function<B, D> h) {
+ return (y) -> f.apply(g.apply(x)).apply(h.apply(y));
+ }
+
+ public static <A, B, C, D, E, F, G> Function<C, Function<D, G>> baldEagle(Function<E, Function<F, G>> f,
+ Function<A, Function<B, E>> g, A x, B y, Function<C, Function<D, F>> h) {
+ return (z) -> (zz) -> f.apply(g.apply(x).apply(y)).apply(h.apply(z).apply(zz));
+ }
+
+ public static <A, B> Function<A, B> warbler(Function<A, Function<A, B>> f) {
+ return (x) -> f.apply(x).apply(x);
+ }
+
+ public static <A, B, C> Function<A, Function<B, C>> warblerOnceRemoved(Function<A, Function<B, Function<B, C>>> f) {
+ return (x) -> (y) -> f.apply(x).apply(y).apply(y);
+ }
+
+ public static <A, B, C, D> Function<A, Function<B, Function<C, D>>> warblerTwiceRemoved(
+ Function<A, Function<B, Function<C, Function<C, D>>>> f) {
+ return (x) -> (y) -> (z) -> f.apply(x).apply(y).apply(z).apply(z);
+ }
+
+ public static <A, B, C> Function<A, Function<B, C>> hummingbird(Function<A, Function<B, Function<A, C>>> f) {
+ return (x) -> (y) -> f.apply(x).apply(y).apply(x);
+ }
+
+ public static <A, B> Function<A, Function<B, Function<A, B>>> jay(Function<A, Function<B, B>> f) {
+ return (x1) -> (y) -> (x2) -> f.apply(x1).apply(f.apply(x2).apply(y));
+ }
+
+ public static <A, B, C> Function<A, Function<B, C>> jalt(Function<A, C> f) {
+ return (x) -> (y) -> f.apply(x);
+ }
+
+ public static <A, B, C, D> Function<A, Function<B, Function<C, D>>> jaltPrime(Function<A, Function<B, D>> f) {
+ return (x) -> (y) -> (z) -> f.apply(x).apply(y);
+ }
+
+ public static <A, B, C, D, E> Function<D, Function<E, C>> gamma(
+ Function<Function<A, Function<B, C>>, Function<D, Function<E, B>>> f, Function<A, Function<B, C>> g,
+ Function<D, A> h) {
+ return (dVal) -> (eVal) -> g.apply(h.apply(dVal)).apply(f.apply(g).apply(dVal).apply(eVal));
+ }
+
+ // Mockingbird/Lark aren't expressible easily in a static language
+
+ public static <A, B> Function<Function<A, B>, B> owl(Function<Function<A, B>, A> f) {
+ return (g) -> g.apply(f.apply(g));
+ }
+
+ // Sage has the type-signature (a -> a) -> a, but we need to introduce the
+ // Supplier to add necessary laziness. Otherwise we'd recur without end
+ public static <A> A sage(Function<Supplier<A>, A> f) {
+ return f.apply(() -> sage(f));
+ }
+
+ public static <A, B, C, D> Function<A, Function<B, D>> goldfinch(Function<B, Function<C, D>> f, Function<A, C> h) {
+ return (x) -> (y) -> f.apply(y).apply(h.apply(x));
+ }
+
+ public static <A, B> Function<Function<A, B>, B> thrush(A x) {
+ return (f) -> f.apply(x);
+ }
+
+ public static <A, B, C> Function<B, Function<A, C>> cardinal(Function<A, Function<B, C>> f) {
+ return (y) -> (x) -> f.apply(x).apply(y);
+ }
+
+ public static <A, B, C> Function<Function<A, Function<B, C>>, C> finch(B y, A x) {
+ return (f) -> f.apply(x).apply(y);
+ }
+
+ public static <A, B, C> Function<A, C> robin(B y, Function<A, Function<B, C>> f) {
+ return (x) -> f.apply(x).apply(y);
+ }
+
+ // the merit of having these instead of just a general 'swap' combinator is a
+ // bit questionable
+ public static <A, B, C> Function<Function<A, Function<B, C>>, C> vireo(A x, B y) {
+ return (f) -> f.apply(x).apply(y);
+ }
+
+ // not written here: robin/finch/cardinal/vireo once/twice removed
+ public static <A, B, C> Function<A, C> queer(Function<A, B> f, Function<B, C> g) {
+ return (x) -> g.apply(f.apply(x));
+ }
+
+ public static <A, B, C> Function<Function<A, B>, C> quixotic(Function<B, C> f, A x) {
+ return (g) -> f.apply(g.apply(x));
+ }
+
+ public static <A, B, C> Function<Function<A, B>, C> quizzical(A x, Function<B, C> f) {
+ return (g) -> f.apply(g.apply(x));
+ }
+
+ public static <A, B, C> Function<Function<B, C>, C> quirky(Function<A, B> f, A x) {
+ return (g) -> g.apply(f.apply(x));
+ }
+
+ public static <A, B, C> Function<Function<B, C>, C> quacky(A x, Function<A, B> f) {
+ return (g) -> g.apply(f.apply(x));
+ }
+
+ public static <A, B> Function<Function<A, Function<A, B>>, B> converseWarbler(A x) {
+ return (f) -> f.apply(x).apply(x);
+ }
+}
diff --git a/src/main/java/bjc/functypes/Combinators.java b/src/main/java/bjc/functypes/Combinators.java
index fde13df..416230b 100644
--- a/src/main/java/bjc/functypes/Combinators.java
+++ b/src/main/java/bjc/functypes/Combinators.java
@@ -17,281 +17,276 @@ public class Combinators {
/**
* If-then-else combinator.
*
- * @param <Input> The input type.
+ * @param <Input> The input type.
* @param <Output> The output type.
*
- * @param in The predicate to run.
- * @param ifTrue The condition to run when it is true.
- * @param ifFalse The condition to run when it is false.
+ * @param in The predicate to run.
+ * @param ifTrue The condition to run when it is true.
+ * @param ifFalse The condition to run when it is false.
*
- * @return A function which will invoke one or the other action, based on the predicate.
+ * @return A function which will invoke one or the other action, based on the
+ * predicate.
*/
@SuppressWarnings("unused")
- public static <Input, Output> Function<Input, Output> iftt(
- Predicate<Input> in,
- Function<Input, Output> ifTrue,
- Function<Input, Output> ifFalse)
- {
+ public static <Input, Output> Function<Input, Output> iftt(Predicate<Input> in, Function<Input, Output> ifTrue,
+ Function<Input, Output> ifFalse) {
return arg -> in.test(arg) ? ifTrue.apply(arg) : ifFalse.apply(arg);
}
-
+
/**
* If-then-else expression.
*
* @param <Output> The output type.
*
- * @param in The boolean to use.
- * @param ifTrue The condition to run when it is true.
- * @param ifFalse The condition to run when it is false.
+ * @param in The boolean to use.
+ * @param ifTrue The condition to run when it is true.
+ * @param ifFalse The condition to run when it is false.
*
* @return A value, based on the provided parameter
*/
- public static <Output> Output iftt(
- boolean in,
- Supplier<Output> ifTrue,
- Supplier<Output> ifFalse)
- {
+ public static <Output> Output iftt(boolean in, Supplier<Output> ifTrue, Supplier<Output> ifFalse) {
return in ? ifTrue.get() : ifFalse.get();
}
-
+
/**
* Execute an action before calling a function.
*
- * @param <Input> The input to the function.
+ * @param <Input> The input to the function.
* @param <Output> The output from the function.
*
- * @param action The action to run on the input.
+ * @param action The action to run on the input.
* @param terminal The function to call.
*
* @return A function that runs the provided action before calling the function.
*/
- public static <Input, Output> Function<Input, Output> beforeThis(
- Consumer<Input> action,
- Function<Input, Output> terminal)
- {
+ public static <Input, Output> Function<Input, Output> beforeThis(Consumer<Input> action,
+ Function<Input, Output> terminal) {
return (arg) -> {
action.accept(arg);
return terminal.apply(arg);
};
}
-
+
/**
* Execute an action after calling a function.
*
- * @param <Input> The input to the function.
+ * @param <Input> The input to the function.
* @param <Output> The output to the function.
*
- * @param initial The function to call.
- * @param action The action to call after doing the function.
+ * @param initial The function to call.
+ * @param action The action to call after doing the function.
*
* @return A function that calls the provided action after the function.
*/
- public static <Input, Output> Consumer<Input> andThen(
- Function<Input, Output> initial,
- Consumer<Output> action)
- {
+ public static <Input, Output> Consumer<Input> andThen(Function<Input, Output> initial, Consumer<Output> action) {
return (arg) -> action.accept(initial.apply(arg));
}
-
+
/**
* Standalone function composer.
*
- * @param <Left> The input type of the initial function.
+ * @param <Left> The input type of the initial function.
* @param <Middle> The shared input/output type.
- * @param <Right> The output type of the terminal function.
+ * @param <Right> The output type of the terminal function.
*
- * @param initial The first function to call.
+ * @param initial The first function to call.
* @param terminal The second function to call.
*
* @return A function that composes the provided functions together.
*/
- public static <Left, Middle, Right> Function<Left, Right> compose(
- Function<Left, Middle> initial,
- Function<Middle, Right> terminal)
- {
+ public static <Left, Middle, Right> Function<Left, Right> compose(Function<Left, Middle> initial,
+ Function<Middle, Right> terminal) {
return (arg) -> terminal.apply(initial.apply(arg));
}
-
+
/**
* Execute a function with some internal state.
*
- * @param <Input> The input type of the function.
+ * @param <Input> The input type of the function.
* @param <Output> The output type of the function.
- * @param <State> The type of the internal state.
+ * @param <State> The type of the internal state.
*
- * @param source The function which provides internal state.
- * @param action The function to call.
+ * @param source The function which provides internal state.
+ * @param action The function to call.
*
* @return A function which hides the production of the internal state.
*/
- public static <Input, Output, State> Function<Input, Output> introducing(
- Supplier<State> source,
- BiFunction<State, Input, Output> action)
- {
+ public static <Input, Output, State> Function<Input, Output> introducing(Supplier<State> source,
+ BiFunction<State, Input, Output> action) {
return (input) -> action.apply(source.get(), input);
}
-
+
/**
* Invoke a given function with null to produce its result.
*
- * @param <Input> The input type of the function.
+ * @param <Input> The input type of the function.
* @param <Output> The output type of the function.
*
- * @param action The function to invoke.
+ * @param action The function to invoke.
*
* @return The result of calling the function with null.
*/
- public static <Input, Output> Output invoke(Function<Input, Output> action)
- {
+ public static <Input, Output> Output invoke(Function<Input, Output> action) {
return action.apply(null);
}
-
+
/**
* Execute an action a given number of times.
*
- * @param times The number of times to execute the action.
+ * @param times The number of times to execute the action.
* @param action The action to execute.
*/
- public static void times(int times, Consumer<Integer> action)
- {
- for (int i = 0; i < times; i++) action.accept(i);
+ public static void times(int times, Consumer<Integer> action) {
+ for (int i = 0; i < times; i++)
+ action.accept(i);
}
-
+
+ /**
+ * Construct a function that applies the given operator a fixed number of times.
+ *
+ * @param <T> The type of the data
+ *
+ * @param times The number of times to execute
+ * @param func The function to apply
+ *
+ * @return A function that applies the given one the specified number of times.
+ */
+ public static <T> UnaryOperator<T> times(int times, UnaryOperator<T> func) {
+ return (val) -> {
+ T retVal = val;
+ for (int i = 0; i < times; i++)
+ retVal = func.apply(retVal);
+ return retVal;
+ };
+ }
+
/**
* Invoke a wrapper instead of invoking a normal function.
*
- * @param <Input> The input type to the function.
+ * @param <Input> The input type to the function.
* @param <Output> The output type to the function.
*
* @param function The function to apply.
- * @param wrapper The wrapper around a function.
+ * @param wrapper The wrapper around a function.
*
* @return A function that invokes the wrapper instead.
*/
- public static <Input, Output> Function<Input, Output> around(
- Function<Input, Output> function,
- BiFunction<Input, Function<Input, Output>, Output> wrapper)
- {
+ public static <Input, Output> Function<Input, Output> around(Function<Input, Output> function,
+ BiFunction<Input, Function<Input, Output>, Output> wrapper) {
return (input) -> wrapper.apply(input, function);
}
-
+
/**
* Only run a given function when the argument satisfies a condition.
*
- * @param <Input> The input type of the function.
+ * @param <Input> The input type of the function.
* @param <Output> The output type of the function.
*
* @param function The function to run.
- * @param guard The guard to use for checking the input.
+ * @param guard The guard to use for checking the input.
*
- * @return A function which takes the given input, and only calls the
- * function if the guard returns true. Otherwise, it will return
- * an empty optional.
+ * @return A function which takes the given input, and only calls the function
+ * if the guard returns true. Otherwise, it will return an empty
+ * optional.
*/
- public static <Input, Output> Function<Input, Optional<Output>> provided(
- Function<Input, Output> function,
- Predicate<Input> guard)
- {
- return iftt(guard,
- (arg) -> Optional.ofNullable(function.apply(arg)),
- (ignored) -> Optional.empty()
- );
+ public static <Input, Output> Function<Input, Optional<Output>> provided(Function<Input, Output> function,
+ Predicate<Input> guard) {
+ return iftt(guard, (arg) -> Optional.ofNullable(function.apply(arg)), (ignored) -> Optional.empty());
}
-
+
/**
* Concatenate two functions together, so that they run on the same argument.
*
- * @param <Input> The type of the input.
+ * @param <Input> The type of the input.
* @param <Output1> The type of the first output.
* @param <Output2> The type of the second output.
*
- * @param funcA The first function to call.
- * @param funcB The second function to call.
+ * @param funcA The first function to call.
+ * @param funcB The second function to call.
*
* @return A function that returns a pair of the results of calling both
* functions.
*/
- public static
- <Input, Output1, Output2> Function<Input, Pair<Output1, Output2>>
- concat(Function<Input, Output1> funcA, Function<Input, Output2> funcB)
- {
+ public static <Input, Output1, Output2> Function<Input, Pair<Output1, Output2>> concat(
+ Function<Input, Output1> funcA, Function<Input, Output2> funcB) {
return (arg) -> Pair.pair(funcA.apply(arg), funcB.apply(arg));
}
-
+
/**
* Concatenate a series of functions together, returning a list of their
* results.
*
- * @param <Input> The input type for the functions.
+ * @param <Input> The input type for the functions.
* @param <Output> The output type for the functions.
*
- * @param funcs The series of functions to call.
+ * @param funcs The series of functions to call.
*
- * @return A function that calls each of those functions, and returns a
- * list of their results.
+ * @return A function that calls each of those functions, and returns a list of
+ * their results.
*/
@SafeVarargs
- public static
- <Input, Output> Function<Input, List<Output>>
- concat(Function<Input, Output>... funcs)
- {
+ public static <Input, Output> Function<Input, List<Output>> concat(Function<Input, Output>... funcs) {
List<Function<Input, Output>> funcList = Arrays.asList(funcs);
-
+
// Kind of a nuisance that Java can't properly guess the type of
// our mapper function, but oh well.
-
- return (arg) ->
- funcList.stream()
- .map((Function<Function<Input, Output>, Output>)
- (func) -> func.apply(arg))
- .collect(toList());
+
+ return (arg) -> funcList.stream().map((Function<Function<Input, Output>, Output>) (func) -> func.apply(arg))
+ .collect(toList());
}
-
+
/**
* Return a function that does a series of actions upon a value, then returns
* that value.
*
- * @param <Type> The type given as an argument
+ * @param <Type> The type given as an argument
*
* @param consumers The actions to perform on the value.
*
* @return A function that performs those arguments on a value.
*/
@SafeVarargs
- public static <Type> Function<Type, Type> doWith(Consumer<Type>... consumers)
- {
+ public static <Type> Function<Type, Type> doWith(Consumer<Type>... consumers) {
return (arg) -> {
- for (Consumer<Type> consumer : consumers) consumer.accept(arg);
+ for (Consumer<Type> consumer : consumers)
+ consumer.accept(arg);
return arg;
};
}
-
+
/**
* Perform a series of actions upon a value, then return that value.
*
- * @param <Type> The type given as an argument
+ * @param <Type> The type given as an argument
*
- * @param input The value to use.
+ * @param input The value to use.
* @param consumers The actions to perform on the value.
*
* @return A function that performs those arguments on a value.
*/
@SafeVarargs
- public static <Type> Type with(Type input, Consumer<Type>... consumers)
- {
+ public static <Type> Type with(Type input, Consumer<Type>... consumers) {
return doWith(consumers).apply(input);
}
-
+
/**
* Convert a function into a consumer, ignoring its output.
*
* @param <Input> The input to the function.
*
- * @param func The function to convert.
+ * @param func The function to convert.
*
* @return A consumer which calls the function, and ignores the output.
*/
public static <Input> Consumer<Input> ignore(Function<Input, ?> func) {
return (inp) -> func.apply(inp);
}
+
+ public static <Input, Output> Function<Supplier<Input>, Output> lazify(Function<Input, Output> f) {
+ return (supp) -> f.apply(supp.get());
+ }
+
+ public static <Input, Output> Function<Input, Output> strictify(Function<Supplier<Input>, Output> f) {
+ return (val) -> f.apply(() -> val);
+ }
} \ No newline at end of file
diff --git a/src/main/java/bjc/functypes/ElseFunction.java b/src/main/java/bjc/functypes/ElseFunction.java
new file mode 100644
index 0000000..b37fba8
--- /dev/null
+++ b/src/main/java/bjc/functypes/ElseFunction.java
@@ -0,0 +1,68 @@
+package bjc.functypes;
+
+import java.util.Optional;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+/**
+ * 'Grammar' type used for making certain types of conditional processing read
+ * easier.
+ *
+ * @author bjcul
+ *
+ * @param <Input> The input type
+ * @param <Output> The output type
+ */
+@FunctionalInterface
+public interface ElseFunction<Input, Output> extends Function<Input, Output> {
+ /**
+ * Apply the alternative.
+ *
+ * Defaults to just applying this function
+ *
+ * @param x The input
+ *
+ * @return The result of applying the function
+ */
+ public default Output elseApply(Input x) {
+ return apply(x);
+ }
+
+ /**
+ * Handle a value present in an optional.
+ *
+ * @param <Input> The type contained in the optional.
+ *
+ * @param val The optional
+ *
+ * @return A function for processing the optional
+ */
+ public static <Input> ElseFunction<Consumer<Input>, ElseFunction<Runnable, Void>> ifPresent(Optional<Input> val) {
+ return (f) -> (g) -> {
+ if (val.isPresent())
+ f.accept(val.get());
+ else
+ g.run();
+ return null;
+ };
+ }
+
+ /**
+ * Handle a value not present in an optional.
+ *
+ * @param <Input> The type contained in the optional.
+ *
+ * @param val The optional
+ *
+ * @return A function for processing the optional
+ */
+ public static <Input> ElseFunction<Runnable, ElseFunction<Consumer<Input>, Void>> ifNotPresent(Optional<Input> val) {
+ return (f) -> (g) -> {
+ if (val.isPresent())
+ g.accept(val.get());
+ else
+ f.run();
+ return null;
+ };
+ }
+}
diff --git a/src/main/java/bjc/functypes/ForEach.java b/src/main/java/bjc/functypes/ForEach.java
new file mode 100644
index 0000000..5c39298
--- /dev/null
+++ b/src/main/java/bjc/functypes/ForEach.java
@@ -0,0 +1,115 @@
+package bjc.functypes;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.*;
+import java.util.function.BiFunction;
+
+/**
+ * Parallel forEach
+ *
+ * @author bjcul
+ *
+ * @param <Output> The type output by each iteration
+ * @param <Index> The type indexing each iteration
+ * @param <Input> The type fed to each iteration
+ */
+public abstract class ForEach<Output, Index, Input> {
+ protected static final Exception CONTINUE = new Exception("forEach Continue");
+ // we don't actually care about the stack trace
+ static {
+ CONTINUE.setStackTrace(new StackTraceElement[0]);
+ };
+
+ /**
+ * Run a single instance of the loop
+ *
+ * Throw CONTINUE to cancel the instance without yielding a value
+ *
+ * @param idx The index for the loop
+ * @param in The value to process for the loop
+ * @return The output of the loop
+ *
+ * @throws Exception If something goes wrong in the loop.
+ */
+ public abstract Output block(Index idx, Input in) throws Exception;
+
+ Callable<Output> loop(Index idx, Input in) {
+ return () -> block(idx, in);
+ }
+
+ /**
+ * Create a ForEach from a function
+ *
+ * @param <Output> The type output by each iteration
+ * @param <Index> The type indexing each iteration
+ * @param <Input> The type fed to each iteration
+ *
+ * @param func The function for an iteration
+ *
+ * @return A ForEach backed by the given function
+ */
+ public static <Output, Index, Input> ForEach<Output, Index, Input> from(BiFunction<Index, Input, Output> func) {
+ return new FunctionalForEach<>(func);
+ }
+
+ /**
+ * Execute a parallel forEach.
+ *
+ * @param <Output> The type output by each iteration
+ * @param <Index> The type indexing each iteration
+ * @param <Input> The type fed to each iteration
+ *
+ * @param pool The concurrent executor
+ * @param in The collection to iterate over
+ * @param block The block to run an iteration
+ *
+ * @return A map representing the results of each iteration.
+ */
+ public static <Output, Index, Input> LinkedHashMap<Index, Output> forEach(ExecutorService pool,
+ Map<Index, Input> in, ForEach<Output, Index, Input> block) {
+ int size = in.size();
+
+ final Map<Index, Future<Output>> futures = new LinkedHashMap<>(size);
+ for (Entry<Index, Input> entry : in.entrySet()) {
+ Index key = entry.getKey();
+ futures.put(key, pool.submit(block.loop(key, entry.getValue())));
+ }
+ final LinkedHashMap<Index, Output> results = new LinkedHashMap<>(size);
+ for (Entry<Index, Future<Output>> entry : futures.entrySet()) {
+ Index key = entry.getKey();
+ try {
+ results.put(key, entry.getValue().get());
+ } catch (ExecutionException eex) {
+ Throwable cause = eex.getCause();
+ if (cause != CONTINUE) {
+ // CONTINUE is used to exit a given loop, so ignore it
+ futures.values().forEach((f) -> f.cancel(true));
+ throw new IllegalStateException(
+ "Exception thrown evaluating forEach index " + key + " of value " + in.get(key), cause);
+ }
+ } catch (CancellationException cex) {
+ // Ignore these
+ } catch (InterruptedException iex) {
+ // Carryover interrupt
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ return results;
+ }
+}
+
+final class FunctionalForEach<Output, Index, Input> extends ForEach<Output, Index, Input> {
+ private final BiFunction<Index, Input, Output> func;
+
+ FunctionalForEach(BiFunction<Index, Input, Output> func) {
+ this.func = func;
+ }
+
+ @Override
+ public Output block(Index idx, Input in) throws Exception {
+ return func.apply(idx, in);
+ }
+}
diff --git a/src/main/java/bjc/functypes/TriFunction.java b/src/main/java/bjc/functypes/TriFunction.java
new file mode 100644
index 0000000..e93e32d
--- /dev/null
+++ b/src/main/java/bjc/functypes/TriFunction.java
@@ -0,0 +1,91 @@
+package bjc.functypes;
+
+import java.util.function.BiFunction;
+import java.util.function.Function;
+
+/**
+ * Represents a function which takes 3 input values.
+ *
+ * @author bjcul
+ *
+ * @param <In1> The first input type
+ * @param <In2> The second input type
+ * @param <In3> The third input type
+ * @param <Out> The output type
+ */
+@FunctionalInterface
+public interface TriFunction<In1, In2, In3, Out> {
+ /**
+ * Apply this function.
+ *
+ * @param x The first input
+ * @param y The second input
+ * @param z The third input
+ *
+ * @return The output
+ */
+ public Out apply(In1 x, In2 y, In3 z);
+
+ // TODO: come up with some way to implement a uncurry that won't stack on top of a curry
+
+ /**
+ * Curry the first argument for this function.
+ *
+ * @return The function with the first argument curried.
+ */
+ public default Function<In1, BiFunction<In2, In3, Out>> curryFirst() {
+ return (x) -> (y, z) -> apply(x, y, z);
+ }
+
+ /**
+ * Curry the third argument for this function.
+ *
+ * @return The function with the third argument curried.
+ */
+ public default BiFunction<In1, In2, Function<In3, Out>> curryLast() {
+ return (x, y) -> (z) -> apply(x, y, z);
+ }
+
+ /**
+ * Fully curry this function
+ *
+ * @return The fully curried function
+ */
+ public default Function<In1, Function<In2, Function<In3, Out>>> fullCurry() {
+ return (x) -> (y) -> (z) -> apply(x, y, z);
+ }
+
+ /**
+ * Partially apply the third argument.
+ *
+ * @param z The value for the third argument
+ *
+ * @return The function w/ the third argument partially applied
+ */
+ public default BiFunction<In1, In2, Out> partialLast(In3 z) {
+ return (x, y) -> apply(x, y, z);
+ }
+
+ public default BiFunction<In1, In3, Out> partialMiddle(In2 y) {
+ return (x, z) -> apply(x, y, z);
+ }
+
+ public default BiFunction<In2, In3, Out> partialFirst(In1 x) {
+ return (y, z) -> apply(x, y, z);
+ }
+
+ /**
+ * Apply a transform to the output of this function
+ *
+ * @param <Out2> The new output type
+ *
+ * @param f The mapping function
+ *
+ * @return The function with its outputs transformed
+ */
+ public default <Out2> TriFunction<In1, In2, In3, Out2> mapOutput(Function<Out, Out2> f) {
+ return (x, y, z) -> f.apply(apply(x, y, z));
+ }
+ // Possible additions: partial applications which take a Supplier, functions to
+ // determine one/two of the args based on the others, functions to map one of the args
+}
diff --git a/src/main/java/bjc/functypes/Unit.java b/src/main/java/bjc/functypes/Unit.java
new file mode 100644
index 0000000..75bdf62
--- /dev/null
+++ b/src/main/java/bjc/functypes/Unit.java
@@ -0,0 +1,17 @@
+package bjc.functypes;
+
+/**
+ * The class that exists, but does nothing else.
+ * @author bjcul
+ *
+ */
+public class Unit {
+ /**
+ * The single instance of Unit that exists
+ */
+ public static final Unit UNIT = new Unit();
+
+ private Unit() {
+ // Empty
+ }
+}