diff options
| author | Ben Culkin <scorpress@gmail.com> | 2020-12-01 20:20:27 -0500 |
|---|---|---|
| committer | Ben Culkin <scorpress@gmail.com> | 2020-12-01 20:20:27 -0500 |
| commit | 097a33bc2ecaa64a664550ddd62ccd8de47c51d0 (patch) | |
| tree | df9680c1f33fddf4dcbe538593ee73703afb91ce | |
| parent | c85db1bef75e5c9b7287ab7fdb6e1380d577c674 (diff) | |
An assortment of changes
| -rw-r--r-- | src/example/java/bjc/functypes/FixpointExample.java | 37 | ||||
| -rw-r--r-- | src/main/java/bjc/data/Contexts.java | 23 | ||||
| -rw-r--r-- | src/main/java/bjc/data/IContext.java | 44 | ||||
| -rw-r--r-- | src/main/java/bjc/esodata/TapeLibrary.java | 163 | ||||
| -rw-r--r-- | src/main/java/bjc/esodata/TapeView.java | 102 | ||||
| -rw-r--r-- | src/main/java/bjc/functypes/Combinators.java | 171 | ||||
| -rw-r--r-- | src/main/java/bjc/functypes/FunctionalIsomorphism.java | 50 | ||||
| -rw-r--r-- | src/main/java/bjc/functypes/ID.java | 2 | ||||
| -rw-r--r-- | src/main/java/bjc/functypes/Isomorphism.java | 54 | ||||
| -rw-r--r-- | src/main/java/bjc/functypes/ThrowFunction.java | 85 | ||||
| -rw-r--r-- | src/main/java/bjc/functypes/Thrower.java | 165 | ||||
| -rw-r--r-- | src/test/java/bjc/functypes/IDTest.java | 7 |
12 files changed, 870 insertions, 33 deletions
diff --git a/src/example/java/bjc/functypes/FixpointExample.java b/src/example/java/bjc/functypes/FixpointExample.java index 8d3e658..71dab4a 100644 --- a/src/example/java/bjc/functypes/FixpointExample.java +++ b/src/example/java/bjc/functypes/FixpointExample.java @@ -1,17 +1,28 @@ package bjc.functypes; +import static bjc.functypes.Combinators.*; + import java.util.function.*; -public class FixpointExample { - public static void main(String[] args) { - BiFunction<Integer, Function<Integer, Integer>, Integer> func - = (input, self) -> { - if (input <= 1) return 1; - else return input * self.apply(input - 1); - }; - - Function<Integer, Integer> factorial = Fixpoints.fix(func); - - for (int i = 0; i < 10; i++) System.out.println(factorial.apply(i)); - } -} +/** + * Examples about how fixpoints work. + * + * @author Ben Culkin + * + */ +public class FixpointExample +{ + /** + * Main method. + * + * @param args Unused CLI args. + */ + public static void main(String[] args) { + Function<Integer, Integer> factorial = Fixpoints.fix((input, self) -> { + if (input <= 1) return 1; + else return input * self.apply(input - 1); + }); + + times(10, andThen(factorial::apply, System.out::println)); + } +}
\ No newline at end of file diff --git a/src/main/java/bjc/data/Contexts.java b/src/main/java/bjc/data/Contexts.java index 0769447..75c8480 100644 --- a/src/main/java/bjc/data/Contexts.java +++ b/src/main/java/bjc/data/Contexts.java @@ -2,17 +2,38 @@ package bjc.data; import java.util.*; +/** + * Utility methods for dealing with contexts. + * + * @author Ben Culkin + * + */ public class Contexts { + /** + * The null context, which always throws an exception. + */ public static final IContext NULL = new NullContextImpl(); private Contexts() { throw new UnsupportedOperationException(); } + /** + * Create a new context with no parent. + * + * @return A context with no parent. + */ public static IContext create() { return new ContextImpl(NULL); } + /** + * Create a context with the specified parent. + * + * @param parent The parent of this context. + * + * @return A context with the given context as its parent. + */ public static IContext create(IContext parent) { return new ContextImpl(parent); } @@ -47,7 +68,7 @@ public class Contexts { public ContextImpl(IContext parent) { this.parent = parent; - this.objects = new HashMap<String, Object>(); + this.objects = new HashMap<>(); } @Override diff --git a/src/main/java/bjc/data/IContext.java b/src/main/java/bjc/data/IContext.java index a1073f1..e519501 100644 --- a/src/main/java/bjc/data/IContext.java +++ b/src/main/java/bjc/data/IContext.java @@ -1,14 +1,58 @@ package bjc.data; +/** + * Represents a 'context' which is a hierarchical set of objects. + * @author Ben Culkin + * + */ public interface IContext { + /** + * Register an object with this context. + * + * @param name The name of the object. + * @param o The object to register. + */ void register(String name, Object o); + /** + * Get the parent of this context. + * + * @return The parent of this context. + */ IContext getParent(); + /** + * Get an object from this context. + * + * @param name The name of the object. + * + * @return The object bound to that name. + */ Object get(String name); + /** + * Get an object which is an instance of the provided class or a subclass + * thereof. + * + * @param <T> The type of the object. + * + * @param contract The class of the object. + * + * @return An instance of the provided class. + */ <T> T get(Class<T> contract); + /** + * Get a named object which is an instance of the provided class or a subclass + * thereof. + * + * @param <T> The type of the object. + * + * @param name The name of the object + * @param contract The class of the object. + * + * @return An instance of the provided class, with the given name.. + */ default <T> T get(String name, Class<T> contract) { Object obj = get(name); return obj == null diff --git a/src/main/java/bjc/esodata/TapeLibrary.java b/src/main/java/bjc/esodata/TapeLibrary.java new file mode 100644 index 0000000..922833f --- /dev/null +++ b/src/main/java/bjc/esodata/TapeLibrary.java @@ -0,0 +1,163 @@ +package bjc.esodata; + +import java.util.*; + +/** + * Represents a library of possible tapes, with a single tape 'mounted' or active + * at a time. + * + * @author Ben Culkin + * + * @param <ElementType> The type stored on each tape. + */ +public class TapeLibrary<ElementType> implements TapeView<ElementType> +{ + private final Map<String, Tape<ElementType>> library; + + private String currentLabel; + private Tape<ElementType> currentTape; + + private boolean allowAutoCreation; + + /** + * Get a view of this tape library as a map. + * + * Modifications to this map will apply to the library, but will not + * affect whether a given tape is mounted or not. Be forewarned. + * + * @return A view onto this tape library as a map. + */ + public Map<String, Tape<ElementType>> asMap() + { + return library; + } + + /** + * Create a new empty tape library, with no tape mounted. + */ + public TapeLibrary() + { + library = new HashMap<>(); + } + + /** + * Create a new tape, with a given tape mounted by default. + * + * @param label The label of the tape. + * @param tape The tape to mount. + */ + public TapeLibrary(String label, Tape<ElementType> tape) + { + this(); + library.put(label, tape); + + this.currentLabel = label; + this.currentTape = tape; + } + + @Override + public Tape<ElementType> tapeView() + { + return currentTape; + } + + /** + * Insert a tape into this library. + * + * @param label The label to use for the tape. + * @param tape The tape to add. + * + * @return The tape which previously had that label, or null if there was not one. + */ + public Tape<ElementType> insertTape(String label, Tape<ElementType> tape) + { + return library.put(label, tape); + } + + /** + * Remove a tape from this library. + * + * @param label The label of the tape to remove. + * + * @return The tape which had that label, or null if there was not one. + */ + public Tape<ElementType> removeTape(String label) + { + return library.remove(label); + } + + /** + * Check if this library has a tape with a given label. + * + * @param label The label of the tape to check for. + * + * @return Whether or not the library contains a tape with that label. + */ + public boolean hasTape(String label) + { + return allowAutoCreation ? true : library.containsKey(label); + } + + /** + * Mount a different tape in the library. + * + * @param label The label of the tape to mount. + * + * @return True if the tape was successfully mounted, false otherwise. + */ + public boolean mountTape(String label) + { + if (library.containsKey(label) || allowAutoCreation) + { + currentLabel = label; + currentTape = library.computeIfAbsent( + label, + (ignored) -> new SingleTape<>()); + + return true; + } else { + return false; + } + } + + /** + * Returns the label of the current tape. + * + * @return The label of the current tape, or null if no tape is + * currently 'mounted'. + */ + public String currentLabel() + { + return currentLabel; + } + + /** + * Unmount the currently mounted tape. + */ + public void ejectTape() + { + currentTape = null; + currentLabel = null; + } + + /** + * Check if this tape library currently allows auto-creation of + * non-existing tapes. + * + * @return Whether or not auto-creation of tapes is currently allowed. + */ + public boolean isAllowAutoCreation() { + return allowAutoCreation; + } + + /** + * Set whether or not this library allows auto-creation of non-existing + * tapes. + * + * @param allowAutoCreation Whether tape auto-creation is allowed. + */ + public void setAllowAutoCreation(boolean allowAutoCreation) + { + this.allowAutoCreation = allowAutoCreation; + } +} diff --git a/src/main/java/bjc/esodata/TapeView.java b/src/main/java/bjc/esodata/TapeView.java new file mode 100644 index 0000000..bfc33a9 --- /dev/null +++ b/src/main/java/bjc/esodata/TapeView.java @@ -0,0 +1,102 @@ +package bjc.esodata; + +/** + * An interface which allows you to view a given type as a tape. + * + * @author Ben Culkin + * + * @param <ElementType> The type of element contained in the tape. + */ +public interface TapeView<ElementType> extends Tape<ElementType> +{ + /** + * Return a view of this object as a tape. + * + * @return A view of this object as a tape. + */ + public Tape<ElementType> tapeView(); + + @Override + public default ElementType item() + { + return tapeView().item(); + } + + @Override + public default void item(ElementType itm) + { + tapeView().item(itm); + } + + @Override + public default int size() + { + return tapeView().size(); + } + + @Override + public default int position() + { + return tapeView().position(); + } + + @Override + public default void insertBefore(ElementType itm) + { + tapeView().insertBefore(itm); + } + + @Override + public default void insertAfter(ElementType itm) + { + tapeView().insertAfter(itm); + } + + @Override + public default ElementType remove() + { + return tapeView().remove(); + } + + @Override + public default void first() + { + tapeView().first(); + } + + @Override + public default void last() + { + tapeView().last(); + } + + @Override + public default boolean left() + { + return tapeView().left(); + } + + @Override + public default boolean left(int amt) + { + return tapeView().left(amt); + } + + @Override + public default boolean right() + { + return tapeView().right(); + } + + @Override + public default boolean right(int amt) + { + return tapeView().right(amt); + } + + @Override + public default boolean seekTo(int pos) + { + return tapeView().seekTo(pos); + } +} diff --git a/src/main/java/bjc/functypes/Combinators.java b/src/main/java/bjc/functypes/Combinators.java index 21238c2..5632b70 100644 --- a/src/main/java/bjc/functypes/Combinators.java +++ b/src/main/java/bjc/functypes/Combinators.java @@ -1,7 +1,12 @@ package bjc.functypes; +import static java.util.stream.Collectors.*; + +import java.util.*; import java.util.function.*; +import bjc.data.*; + /** * A bunch of only slightly related function combinators. * @@ -42,7 +47,8 @@ public class Combinators { * @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) + Consumer<Input> action, + Function<Input, Output> terminal) { return (arg) -> { action.accept(arg); @@ -62,7 +68,8 @@ public class Combinators { * @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) + Function<Input, Output> initial, + Consumer<Output> action) { return (arg) -> action.accept(initial.apply(arg)); } @@ -130,4 +137,164 @@ public class Combinators { { for (int i = 0; i < times; i++) action.accept(i); } + + /** + * Invoke a wrapper instead of invoking a normal 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. + * + * @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) + { + 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 <Output> The output type of the function. + * + * @param function The function to run. + * @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. + */ + 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 <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. + * + * @return A function that returns a pair of the results of calling both + * functions. + */ + public static + <Input, Output1, Output2> Function<Input, IPair<Output1, Output2>> + concat(Function<Input, Output1> funcA, Function<Input, Output2> funcB) + { + return (arg) -> IPair.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 <Output> The output type for the functions. + * + * @param funcs The series of functions to call. + * + * @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) + { + 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 a function that does a series of actions upon a value, then returns + * that value. + * + * @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) + { + return (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 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) + { + return doWith(consumers).apply(input); + } + + /** + * 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 functions 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(Function<Type, ?>... functions) + { + return (arg) -> { + for (Function<Type, ?> function : functions) function.apply(arg); + return arg; + }; + } + + /** + * Perform a series of actions upon a value, then return that value. + * + * @param <Type> The type given as an argument + * + * @param input The value to use. + * @param functions 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, Function<Type, ?>... functions) + { + return doWith(functions).apply(input); + } }
\ No newline at end of file diff --git a/src/main/java/bjc/functypes/FunctionalIsomorphism.java b/src/main/java/bjc/functypes/FunctionalIsomorphism.java new file mode 100644 index 0000000..8518338 --- /dev/null +++ b/src/main/java/bjc/functypes/FunctionalIsomorphism.java @@ -0,0 +1,50 @@ +package bjc.functypes; + +import java.util.function.Function; + +/** + * A pair of functions to transform between a pair of types. + * + * @author bjculkin + * + * @param <Source> + * The source type of the isomorphism. + * + * @param <Dest> + * The destination type of isomorphism. + */ +public class FunctionalIsomorphism<Source, Dest> implements Isomorphism<Source, Dest> +{ + /* The function to the destination type. */ + private Function<Source, Dest> toFunc; + /* The function to the source type. */ + private Function<Dest, Source> fromFunc; + + /** + * Create a new isomorphism. + * + * @param to + * The 'forward' function, from the source to the definition. + * + * @param from + * The 'backward' function, from the definition to the source. + */ + public FunctionalIsomorphism(Function<Source, Dest> to, + Function<Dest, Source> from) + { + toFunc = to; + fromFunc = from; + } + + @Override + public Dest to(Source val) + { + return toFunc.apply(val); + } + + @Override + public Source from(Dest val) + { + return fromFunc.apply(val); + } +} diff --git a/src/main/java/bjc/functypes/ID.java b/src/main/java/bjc/functypes/ID.java index f830d66..53a4c84 100644 --- a/src/main/java/bjc/functypes/ID.java +++ b/src/main/java/bjc/functypes/ID.java @@ -10,6 +10,8 @@ import java.util.function.UnaryOperator; public class ID { /** * Create an identity function. + * + * @param <A> The type of the function. * * @return A identity function. */ diff --git a/src/main/java/bjc/functypes/Isomorphism.java b/src/main/java/bjc/functypes/Isomorphism.java new file mode 100644 index 0000000..db633e6 --- /dev/null +++ b/src/main/java/bjc/functypes/Isomorphism.java @@ -0,0 +1,54 @@ +package bjc.functypes; + +import java.util.function.*; + +/** + * Denotes that two types are essentially equivalent, by providing a means to + * convert between them. + * + * @author Ben Culkin + * + * @param <Source> The first type. + * @param <Dest> The second type. + */ +public interface Isomorphism<Source, Dest> +{ + + /** + * Apply the isomorphism forward. + * + * @param val + * The source value. + * + * @return The destination value. + */ + Dest to(Source val); + + /** + * Apply the isomorphism backward. + * + * @param val + * The destination value. + * + * @return The source value. + */ + Source from(Dest val); + + /** + * Create an isomorphism from a pair of functions. + * + * @param <Src> The source type. + * @param <Dst> The destination type. + * + * @param forward The function from source to destination. + * @param backward The function from destination to source. + * + * @return An isomorphism between the two types. + */ + static <Src, Dst> Isomorphism<Src, Dst> from( + Function<Src, Dst> forward, + Function<Dst, Src> backward) + { + return new FunctionalIsomorphism<>(forward, backward); + } +}
\ No newline at end of file diff --git a/src/main/java/bjc/functypes/ThrowFunction.java b/src/main/java/bjc/functypes/ThrowFunction.java index b72d735..132f55c 100644 --- a/src/main/java/bjc/functypes/ThrowFunction.java +++ b/src/main/java/bjc/functypes/ThrowFunction.java @@ -11,7 +11,8 @@ import java.util.function.*; * @param <ReturnType> The output to the function. * @param <ExType> The type of exception thrown. */ -public interface ThrowFunction<InputType, ReturnType, ExType extends Throwable> { +public interface ThrowFunction<InputType, ReturnType, ExType extends Throwable> +{ /** * Does the possibly throwing computation embodied by this function. * @@ -38,15 +39,20 @@ public interface ThrowFunction<InputType, ReturnType, ExType extends Throwable> */ @SuppressWarnings("unchecked") default Function<InputType, ReturnType> swallow( - Class<ExType> clasz, Function<ExType, ReturnType> handler) { - return (inp) -> { - try { + Class<ExType> clasz, Function<ExType, ReturnType> handler) + { + return (inp) -> + { + try + { return this.apply(inp); } catch (Throwable ex) { - if (clasz.isInstance(ex)) { + if (clasz.isInstance(ex)) + { // Swallow this return handler.apply((ExType) ex); - } else { + } else + { String msg = "Exception of incorrect type to be handled, only " + clasz.getName() + " are handled"; @@ -68,14 +74,19 @@ 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 { + 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"; @@ -85,4 +96,56 @@ public interface ThrowFunction<InputType, ReturnType, ExType extends Throwable> } }); } + + /** + * Create a {@link Thrower} which will yield the result of calling this + * function with the given argument. + * + * @param arg The argument to use. + * + * @return A thrower which will call this function with the given value. + */ + default Thrower<ReturnType, ExType> suspend(InputType arg) + { + return () -> this.apply(arg); + } + + /** + * Compose two throwing functions together. + * + * @param <NewOutput> The newly output type. + * + * @param func The throwing function to compose this with. + * + * @return A throwing function that composes the two. + */ + default <NewOutput> ThrowFunction<InputType, NewOutput, ExType> + compose( + ThrowFunction<ReturnType, NewOutput, ExType> func) { + return (arg) -> func.apply(this.apply(arg)); + } + + /** + * ThrowFunctions and functions which return a {@link Thrower} are isomorphic. + * + * @param <InpType> The function input type. + * @param <OutType> The function output type. + * @param <ExType> The exception type. + * + * @return The isomorphism between them. + */ + static <InpType, OutType, ExType extends Throwable> + Isomorphism<ThrowFunction<InpType, OutType, ExType>, Function<InpType, Thrower<OutType, ExType>>> + getIso() + { + // @FIXME Nov 23, 2020 Ben Culkin :EquivISO + // Fix this to strip wrappers when appropriate, so that going + // backwards and forwards leaves you where you started, not under + // two levels of indirection. + return Isomorphism.from((throwFun) -> { + return (arg) -> throwFun.suspend(arg); + }, (thrower) -> { + return (arg) -> thrower.apply(arg).extract(); + }); + } } diff --git a/src/main/java/bjc/functypes/Thrower.java b/src/main/java/bjc/functypes/Thrower.java new file mode 100644 index 0000000..eb03eaf --- /dev/null +++ b/src/main/java/bjc/functypes/Thrower.java @@ -0,0 +1,165 @@ +package bjc.functypes; + +import java.util.*; +import java.util.function.*; + +/** + * A source for a value that could produce an exception. + * + * @author Ben Culkin + * + * @param <ValueType> The type of value possibly produced. + * @param <ExceptionType> The type of exception possibly thrown. + */ +@FunctionalInterface +public interface Thrower<ValueType, ExceptionType extends Throwable> +{ + /** + * Attempt to get the value. + * + * @return The value from this source. + * + * @throws ExceptionType If something goes wrong getting the value. + */ + ValueType extract() throws ExceptionType; + + /** + * Converts this thrower into a memoized one. + * + * Note that if this does throw an exception, it won't be 'memoized' so + * that the next call will call this Thrower again. + * + * @return A memoizing thrower. + */ + default Thrower<ValueType, ExceptionType> memoize() { + return new MemoizedThrower<>(this); + } + + /** + * Applies a function to the result of this thrower. + * + * @param <NewOutputType> The new type output by the function. + * + * @param func The function to apply. + * + * @return A thrower that is the result of applying the given function. + */ + default <NewOutputType> Thrower<NewOutputType, ExceptionType> bind( + Function<ValueType, Thrower<NewOutputType, ExceptionType>> func) + { + return () -> func.apply(extract()).extract(); + } + /** + * Create a thrower that yields a given value. + * + * @param <ValType> The type of the value. + * + * @param val The value to yield. + * + * @return A thrower that will always yield that value. + */ + static <ValType> + Thrower<ValType, ? extends Throwable> from(ValType val) + { + return () -> val; + } + + /** + * Create a thrower that yields a given value. + * + * @param <ValType> The type of the value. + * + * @param val The value to yield. + * + * @return A thrower that will always yield that value. + */ + static <ValType> + Thrower<ValType, ? extends Throwable> from(Supplier<ValType> val) + { + return val::get; + } + + /** + * Convert a function on values to one over throwers. + * + * @param <Input> The function input type. + * @param <Output> The function output type. + * @param <ExType> The exception possibly thrown. + * + * @param func The function to convert. + * + * @return A function which operates on throwers instead. + */ + static <Input, Output, ExType extends Throwable> + Function<Thrower<Input, ExType>,Thrower<Output, ExType>> fmap( + Function<Input, Output> func) + { + return (input) -> () -> func.apply(input.extract()); + } + + /** + * Convert a list of throwers into a thrower that returns a list. + * + * @param <Output> The type output by the thrower. + * @param <ExType> The type of exception thrown. + * + * @param throwers The list of throwers. + * + * @return A thrower that returns a list of results. + */ + static <Output, ExType extends Throwable> + Thrower<List<Output>, ExType> seq(List<Thrower<Output, ExType>> throwers) + { + return () -> { + List<Output> results = new ArrayList<>(throwers.size()); + for (Thrower<Output, ExType> thrower : throwers) + { + results.add(thrower.extract()); + } + return results; + }; + } + + /** + * Convert a array of throwers into a thrower that returns a list. + * + * @param <Output> The type output by the thrower. + * @param <ExType> The type of exception thrown. + * + * @param throwers The array of throwers. + * + * @return A thrower that returns a list of results. + */ + @SafeVarargs + static <Output, ExType extends Throwable> + Thrower<List<Output>, ExType> seq(Thrower<Output, ExType>... throwers) + { + return () -> { + List<Output> results = new ArrayList<>(throwers.length); + for (Thrower<Output, ExType> thrower : throwers) + { + results.add(thrower.extract()); + } + return results; + }; + } +} + +class MemoizedThrower<ValueType, ExceptionType extends Throwable> +implements Thrower<ValueType, ExceptionType> +{ + private final Thrower<ValueType, ExceptionType> source; + private ValueType val; + + public MemoizedThrower(Thrower<ValueType, ExceptionType> source) + { + this.source = source; + } + + @Override + public ValueType extract() throws ExceptionType + { + if (val == null) val = source.extract(); + return val; + } +} diff --git a/src/test/java/bjc/functypes/IDTest.java b/src/test/java/bjc/functypes/IDTest.java index 0865282..69c6133 100644 --- a/src/test/java/bjc/functypes/IDTest.java +++ b/src/test/java/bjc/functypes/IDTest.java @@ -6,12 +6,7 @@ import java.util.function.*; import org.junit.*; -/** - * Test for ID. - * - * @author Ben Culkin - * - */ +@SuppressWarnings("javadoc") public class IDTest { @Test |
