From 097a33bc2ecaa64a664550ddd62ccd8de47c51d0 Mon Sep 17 00:00:00 2001 From: Ben Culkin Date: Tue, 1 Dec 2020 20:20:27 -0500 Subject: An assortment of changes --- src/main/java/bjc/data/Contexts.java | 23 ++- src/main/java/bjc/data/IContext.java | 44 ++++++ src/main/java/bjc/esodata/TapeLibrary.java | 163 ++++++++++++++++++++ src/main/java/bjc/esodata/TapeView.java | 102 ++++++++++++ src/main/java/bjc/functypes/Combinators.java | 171 ++++++++++++++++++++- .../java/bjc/functypes/FunctionalIsomorphism.java | 50 ++++++ src/main/java/bjc/functypes/ID.java | 2 + src/main/java/bjc/functypes/Isomorphism.java | 54 +++++++ src/main/java/bjc/functypes/ThrowFunction.java | 85 ++++++++-- src/main/java/bjc/functypes/Thrower.java | 165 ++++++++++++++++++++ 10 files changed, 845 insertions(+), 14 deletions(-) create mode 100644 src/main/java/bjc/esodata/TapeLibrary.java create mode 100644 src/main/java/bjc/esodata/TapeView.java create mode 100644 src/main/java/bjc/functypes/FunctionalIsomorphism.java create mode 100644 src/main/java/bjc/functypes/Isomorphism.java create mode 100644 src/main/java/bjc/functypes/Thrower.java (limited to 'src/main/java/bjc') 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(); + 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 The type of the object. + * + * @param contract The class of the object. + * + * @return An instance of the provided class. + */ T get(Class contract); + /** + * Get a named object which is an instance of the provided class or a subclass + * thereof. + * + * @param 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 get(String name, Class 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 The type stored on each tape. + */ +public class TapeLibrary implements TapeView +{ + private final Map> library; + + private String currentLabel; + private Tape 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> 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 tape) + { + this(); + library.put(label, tape); + + this.currentLabel = label; + this.currentTape = tape; + } + + @Override + public Tape 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 insertTape(String label, Tape 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 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 The type of element contained in the tape. + */ +public interface TapeView extends Tape +{ + /** + * Return a view of this object as a tape. + * + * @return A view of this object as a tape. + */ + public Tape 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 Function beforeThis( - Consumer action, Function terminal) + Consumer action, + Function 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 Consumer andThen( - Function initial, Consumer action) + Function initial, + Consumer 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 The input type to the function. + * @param 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 Function around( + Function function, + BiFunction, Output> wrapper) + { + return (input) -> wrapper.apply(input, function); + } + + /** + * Only run a given function when the argument satisfies a condition. + * + * @param The input type of the function. + * @param 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 Function> provided( + Function function, + Predicate 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 The type of the input. + * @param The type of the first output. + * @param 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 + Function> + concat(Function funcA, Function funcB) + { + return (arg) -> IPair.pair(funcA.apply(arg), funcB.apply(arg)); + } + + /** + * Concatenate a series of functions together, returning a list of their + * results. + * + * @param The input type for the functions. + * @param 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 + Function> + concat(Function... funcs) + { + List> 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, Output>) + (func) -> func.apply(arg)) + .collect(toList()); + } + + /** + * Return a function that does a series of actions upon a value, then returns + * that value. + * + * @param 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 Function doWith(Consumer... consumers) + { + return (arg) -> { + for (Consumer consumer : consumers) consumer.accept(arg); + return arg; + }; + } + + /** + * Perform a series of actions upon a value, then return that value. + * + * @param 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 with(Type input, Consumer... consumers) + { + return doWith(consumers).apply(input); + } + + /** + * Return a function that does a series of actions upon a value, then returns + * that value. + * + * @param 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 Function doWith(Function... functions) + { + return (arg) -> { + for (Function function : functions) function.apply(arg); + return arg; + }; + } + + /** + * Perform a series of actions upon a value, then return that value. + * + * @param 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 with(Type input, Function... 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 + * The source type of the isomorphism. + * + * @param + * The destination type of isomorphism. + */ +public class FunctionalIsomorphism implements Isomorphism +{ + /* The function to the destination type. */ + private Function toFunc; + /* The function to the source type. */ + private Function 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 to, + Function 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 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 The first type. + * @param The second type. + */ +public interface Isomorphism +{ + + /** + * 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 The source type. + * @param 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 Isomorphism from( + Function forward, + Function 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 The output to the function. * @param The type of exception thrown. */ -public interface ThrowFunction { +public interface ThrowFunction +{ /** * Does the possibly throwing computation embodied by this function. * @@ -38,15 +39,20 @@ public interface ThrowFunction */ @SuppressWarnings("unchecked") default Function swallow( - Class clasz, Function handler) { - return (inp) -> { - try { + Class clasz, Function 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 @SuppressWarnings("unchecked") default Function recover( Class clasz, BiFunction 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 } }); } + + /** + * 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 suspend(InputType arg) + { + return () -> this.apply(arg); + } + + /** + * Compose two throwing functions together. + * + * @param The newly output type. + * + * @param func The throwing function to compose this with. + * + * @return A throwing function that composes the two. + */ + default ThrowFunction + compose( + ThrowFunction func) { + return (arg) -> func.apply(this.apply(arg)); + } + + /** + * ThrowFunctions and functions which return a {@link Thrower} are isomorphic. + * + * @param The function input type. + * @param The function output type. + * @param The exception type. + * + * @return The isomorphism between them. + */ + static + Isomorphism, Function>> + 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 The type of value possibly produced. + * @param The type of exception possibly thrown. + */ +@FunctionalInterface +public interface Thrower +{ + /** + * 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 memoize() { + return new MemoizedThrower<>(this); + } + + /** + * Applies a function to the result of this thrower. + * + * @param 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 Thrower bind( + Function> func) + { + return () -> func.apply(extract()).extract(); + } + /** + * Create a thrower that yields a given value. + * + * @param The type of the value. + * + * @param val The value to yield. + * + * @return A thrower that will always yield that value. + */ + static + Thrower from(ValType val) + { + return () -> val; + } + + /** + * Create a thrower that yields a given value. + * + * @param The type of the value. + * + * @param val The value to yield. + * + * @return A thrower that will always yield that value. + */ + static + Thrower from(Supplier val) + { + return val::get; + } + + /** + * Convert a function on values to one over throwers. + * + * @param The function input type. + * @param The function output type. + * @param The exception possibly thrown. + * + * @param func The function to convert. + * + * @return A function which operates on throwers instead. + */ + static + Function,Thrower> fmap( + Function func) + { + return (input) -> () -> func.apply(input.extract()); + } + + /** + * Convert a list of throwers into a thrower that returns a list. + * + * @param The type output by the thrower. + * @param The type of exception thrown. + * + * @param throwers The list of throwers. + * + * @return A thrower that returns a list of results. + */ + static + Thrower, ExType> seq(List> throwers) + { + return () -> { + List results = new ArrayList<>(throwers.size()); + for (Thrower thrower : throwers) + { + results.add(thrower.extract()); + } + return results; + }; + } + + /** + * Convert a array of throwers into a thrower that returns a list. + * + * @param The type output by the thrower. + * @param The type of exception thrown. + * + * @param throwers The array of throwers. + * + * @return A thrower that returns a list of results. + */ + @SafeVarargs + static + Thrower, ExType> seq(Thrower... throwers) + { + return () -> { + List results = new ArrayList<>(throwers.length); + for (Thrower thrower : throwers) + { + results.add(thrower.extract()); + } + return results; + }; + } +} + +class MemoizedThrower +implements Thrower +{ + private final Thrower source; + private ValueType val; + + public MemoizedThrower(Thrower source) + { + this.source = source; + } + + @Override + public ValueType extract() throws ExceptionType + { + if (val == null) val = source.extract(); + return val; + } +} -- cgit v1.2.3