diff --git a/README.md b/README.md new file mode 100644 index 0000000..4e15868 --- /dev/null +++ b/README.md @@ -0,0 +1,994 @@ +# Mon + +Wrapper, TypeAlias, Result, Lazy, Maybe and combinators for Java. + +![GitHub release (latest by date)]( +https://img.shields.io/github/v/release/kemitix/mon?style=for-the-badge) +![GitHub Release Date]( +https://img.shields.io/github/release-date/kemitix/mon?style=for-the-badge) + +[![Nexus]( +https://img.shields.io/nexus/r/https/oss.sonatype.org/net.kemitix/mon.svg?style=for-the-badge)]( +https://oss.sonatype.org/content/repositories/releases/net/kemitix/mon/) +[![Maven-Central]( +https://img.shields.io/maven-central/v/net.kemitix/mon.svg?style=for-the-badge)]( +https://search.maven.org/artifact/net.kemitix/mon) + +- [Maven Usage](#Maven-Usage) +- [Wrapper](#Wrapper) +- [TypeAlias](#TypeAlias) +- [Maybe](#Maybe) +- [Result](#Result) +- [Tree](#Tree) + +--- +## Maven Usage + +``` xml + + net.kemitix + mon + ${mon.version} + +``` + +--- +## Wrapper + +A simple `@FunctionalInterface` that contains a value. Can be used to implement +a form of type-alias in Java. + +In Haskell, it is possible to create an alias for a Type, and to then use +that alias with the same behaviour as the original, except that the compiler +doesn't treat the alias as the same Type and will generate compiler errors +if you try to use them together. e.g.: + +``` haskell +newtype PhoneNumber = PhoneNumber String +newtype Name = Name String +newtype PhoneBookEntry = PhoneBookEntry (Name, PhoneNumber) +newtype PhoneBook = PhoneBook [PhoneBookEntry] +``` + +In Java, we don't have the ability to have that true alias, so `Wrapper` simply +wraps the value within a new type. It's as close as I could get to a Haskell +type alias in Java. + +The benefits of using `Wrapper` in this way are: + +- encapsulation of the wrapped type when passing references through code + that doesn't need to access the actual value, but only to pass it on +- type-safe parameters where you would otherwise be passing generic `String`s, + `Integer`s, `List`s, or other general classes +- less verbose than implementing your own + +### Example + +``` java +interface PhoneNumber extends Wrapper {} + +PhoneNumber pn = () -> "01234 567890"; +String v = pn.value(); +``` + +### Instance Methods + +#### `T value()` + +Returns the value in the `Wrapper`. + +--- +## TypeAlias + +This was a precursor to `Wrapper` and should be considered deprecated. It is +also a form of wrapper, but is also a Monadic wrapper, unlike `Wrapper`. + +### Example + +``` java +class PhoneNumber extends TypeAlias { + private PhoneNumber(final String value) { + super(value); + } + public static PhoneNumber of(final String phoneNumber) { + return new PhoneNumber(phoneNumber); + } +} + +PhoneNumber pn = PhoneNumber.of("01234 567890"); +String v = pn.getValue(); +``` + +### Instance Methods + +#### `final R map(final Function f)` + +Map the `TypeAlias` into another value. + +``` java +StudentId studentId = StudentId.of(123); +String idString = studentId.map(id -> String.valueOf(id)); + +class StudentId extends TypeAlias { + private StudentId(Integer value) { + super(value); + } + static StudentId of(Integer id) { + return new StudentId(id); + } +} +``` +--- +#### `final > U flatMap(final Function f)` + +Map the `TypeAlias` into another `TypeAlias`. + +``` java +StudentId studentId = StudentId.of(123); +StudentName studentName = studentId.flatMap(id -> getStudentName(id)); + +class StudentName extends TypeAlias { + private StudentName(String value) { + super(value); + } + static StudentName of(final String name) { + return new StudentName(name); + } +} +``` +--- +#### `T getValue()` + +Get the value of the `TypeAlias`. + +``` java +String name = studentName.getValue(); +``` +--- +## Maybe + +Allows specifying that a value may or may not be present. Similar to +`Optional`. `Maybe` provides additional methods that `Optional` doesn't (as of +Java 8): `isNothing()`, `stream()`, `ifNothing()` and `match()`. `Maybe` does +not have a `get()` method. + +`Maybe` is a Monad. + +Unlike `Optional`, when a `map()` results in a `null`, the `Maybe` will +continue to be a `Just` (i.e. have a value - that value is `null`). `Optional` +would switch to being empty. + +### Example + +``` java +import net.kemitix.mon.maybe.Maybe; + +import java.util.function.Function; +import java.util.function.Predicate; + +class MaybeExample { + + public static void main(String[] args) { + Maybe.just(countArgs(args)) + .filter(isEven()) + .map(validMessage()) + .match( + just -> System.out.println(just), + () -> System.out.println("Not an valid value") + ); + } + + private static Function validMessage() { + return v -> String.format("Value %d is even", v); + } + + private static Predicate isEven() { + return v -> v % 2 == 0; + } + + private static Integer countArgs(String[] args) { + return args.length; + } +} +``` + +In the above example, the number of command line arguments are counted, if +there are an even number of them then a message is created and printed by +the `Consumer` parameter in the `match` call. If there is an odd number of +arguments, then the filter will return `Maybe.nothing()`, meaning that the +`nothing` drops straight through the `map` and triggers the `Runnable` parameter +in the `match` call. + +### Static Constructors + +#### `static Maybe maybe(T value)` + +Create a Maybe for the value that may or may not be present. + +Where the value is `null`, that is taken as not being present. + +``` java +Maybe just = Maybe.maybe(1); +Maybe nothing = Maybe.maybe(null); +``` +--- +#### `static Maybe just(T value)` + +Create a `Maybe` for the value that is present. + +The `value` must not be `null` or a `NullPointerException` will be thrown. + +``` java +Maybe just = Maybe.just(1); +``` +--- +#### `static Maybe nothing()` + +Create a `Maybe` for a lack of a value. + +``` java +Maybe nothing = Maybe.nothing(); +``` +--- +#### `static Maybe findFirst(Stream stream)` + +Creates a `Maybe` from the first item in the stream, or nothing if the stream +is empty. + +``` java +Maybe just3 = Maybe.findFirst(Stream.of(3, 4, 2, 4)); +Maybe nothing = Maybe.findFirst(Stream.empty()); +``` +--- +### Instance Methods + +#### `Maybe filter(Predicate predicate)` + +Filter a Maybe by the predicate, replacing with Nothing when it fails. + +``` java +Maybe maybe = Maybe.maybe(getValue()) + .filter(v -> v % 2 == 0); +``` +--- +#### ` Maybe map(Function f)` + +Applies the function to the value within the `Maybe`, returning the result +within another `Maybe`. + +``` java +Maybe maybe = Maybe.maybe(getValue()) + .map(v -> v * 100); +``` +--- +#### ` Maybe flatMap(Function> f)` + +Applies the function to the value within the `Maybe`, resulting in another +`Maybe`, then flattens the resulting `Maybe>` into `Maybe`. + +``` java +Maybe maybe = Maybe.maybe(getValue()) + .flatMap(v -> Maybe.maybe(getValueFor(v))); +``` +--- +#### `void match(Consumer just, Runnable nothing)` + +Matches the `Maybe`, either to `just` or `nothing`, and performs either the +`Consumer`, for a `Just` value, or the `Runnable` for a `Nothing` value. + +``` java +Maybe.maybe(getValue()) + .match( + just -> workWithValue(just), + () -> nothingToWorkWith() + ); +``` +--- +#### ` R matchValue(Function justMatcher, Supplier nothingMatcher)` + +Matches the `Maybe`, either `Just` or `Nothing`, and performs either the +`Function`, for a `Just` value, or the `Supplier` for a `Nothing` value, +returning the result. + +``` java +String value = Maybe.maybe(getValue()) + .matchValue( + just -> Integer.toString(just), + () -> "nothing" + ); +``` +--- +#### `T orElse(T otherValue)` + +A value to use when the `Maybe` is `Nothing`. + +``` java +Integer value = Maybe.maybe(getValue()) + .orElse(1); +``` +--- +#### `T orElseGet(Supplier otherValueSupplier)` + +Provide a value to use when the `Maybe` is `Nothing`. + +``` java +Integer value = Maybe.maybe(getValue()) + .orElseGet(() -> getDefaultValue()); +``` +--- +#### `T or(Supplier alternative)` + +Provide an alternative `Maybe` to use when the `Maybe` is `Nothing`. + +``` java +Maybe value = Maybe.maybe(getValue()) + .or(() -> Maybe.just(defaultValue)); +``` +--- +#### `void orElseThrow(Supplier error)` + +Throw the exception if the `Maybe` is `Nothing`. + +``` java +Integer value = Maybe.maybe(getValue()) + .orElseThrow(() -> new RuntimeException("error")); +``` +--- +#### `Maybe peek(Consumer consumer)` + +Provide the value within the `Maybe`, if it exists, to the `Consumer`, and +returns the original `Maybe`. + +``` java +Maybe maybe = Maybe.maybe(getValue()) + .peek(v -> v.foo()); +``` +#### `void ifNothing(Runnable runnable)` + +Run the `Runnable` if the `Maybe` is `Nothing`, otherwise do nothing. + +``` java +Maybe.maybe(getValue()) + .ifNothing(() -> doSomething()); +``` +--- +#### `Stream stream()` + +Converts the `Maybe` into either a single value stream or an empty stream. + +``` java +Stream stream = Maybe.maybe(getValue()) + .stream(); +``` +--- +#### `boolean isJust()` + +Checks if the `Maybe` is a `Just`. + +``` java +boolean isJust = Maybe.maybe(getValue()) + .isJust(); +``` +--- +#### `boolean isNothing()` + +Checks if the `Maybe` is `Nothing`. + +``` java +boolean isNothing = Maybe.maybe(getValue()) + .isNothing(); +``` +--- +#### `Optional toOptional()` + +Convert the `Maybe` to an `Optional`. + +``` java +Optional optional = Maybe.maybe(getValue()) + .toOptional(); +``` +--- +## Result + +Allows handling error conditions without the need to `catch` exceptions. + +When a `Result` is returned from a method, it will contain one of two values. +Either the actual result, or an error in the form of an `Exception`. The +exception is returned within the `Result` and is not thrown. + +`Result` is a Monad. + +### Example + +``` java +import net.kemitix.mon.result.Result; + +import java.io.IOException; + +class ResultExample implements Runnable { + + public static void main(final String[] args) { + new ResultExample().run(); + } + + @Override + public void run() { + Result.of(() -> callRiskyMethod()) + .flatMap(state -> doSomething(state)) + .match( + success -> System.out.println(success), + error -> error.printStackTrace() + ); + } + + private String callRiskyMethod() throws IOException { + return "I'm fine"; + } + + private Result doSomething(final String state) { + return Result.of(() -> state + ", it's all good."); + } + +} +``` + +In the above example the string `"I'm fine"` is returned by +`callRiskyMethod()` within a successful `Result`. The `.flatMap()` call, +unwraps that `Result` and, as it is a success, passes the contents to +`doSomething()`, which in turn returns a `Result` that the `.flatMap()` call +returns. `match()` is called on the `Result` and, being a success, will call +the success `Consumer`. + +Had `callRiskyMethod()` thrown an exception it would have been caught by the +`Result.of()` method, which would then return an error `Result`. An error +`Result` would skip the `flatMap` and continue at the `match()` where it +would have called the error `Consumer`. + +### Static Constructors + +#### `static Result of(Callable callable)` + +Create a `Result` for the output of the `Callable`. + +If the `Callable` throws an `Exception`, then the `Result` will be an error and +will contain that exception. + +This will be the main starting point for most `Result`s where the callable +could throw an `Exception`. + +``` java +Result okay = Result.of(() -> 1); +Result error = Result.of(() -> {throw new RuntimeException();}); +``` +--- +#### `static Result ok(T value)` + +Create a `Result` for a success. + +Use this where you have a value that you want to place into the `Result` context. + +``` java +Result okay = Result.ok(1); +``` +--- +#### `static Result error(Throwable error)` + +Create a `Result` for an error. + +``` java +Result error = Result.error(new RuntimeException()); +``` +--- +### Static Methods + +These static methods provide integration with the `Maybe` class. + +#### `static Maybe toMaybe(Result result)` + +Creates a `Maybe` from the `Result`, where the `Result` is a success, then +the `Maybe` will be a `Just` contain the value of the `Result`. However, if the +`Result` is an error, then the `Maybe` will be `Nothing`. + +``` java +Result result = Result.of(() -> getValue()); +Maybe maybe = Result.toMaybe(result); +``` +--- +#### `static Result fromMaybe(Maybe maybe, Supplier error)` + +Creates a `Result` from the `Maybe`, where the `Result` will be an error +if the `Maybe` is nothing. Where the `Maybe` is nothing, then the +`Supplier` will provide the error for the `Result`. + +``` java +Maybe maybe = Maybe.maybe(getValue()); +Result result = Result.fromMaybe(maybe, + () -> new NoSuchFileException("filename")); +``` +--- +#### `static Result> invert(Maybe> maybeResult)` + +Swaps the `Result` within a `Maybe`, so that `Result` contains a `Maybe`. + +``` java +Maybe> maybe = Maybe.maybe(Result.of(() -> getValue())); +Result> result = Result.invert(maybe); +``` +--- +#### `static Result> flatMapMaybe(Result> maybeResult, Function,Result>> f)` + +Applies the function to the contents of a `Maybe` within the `Result`. + +``` java +Result> result = Result.of(() -> Maybe.maybe(getValue())); +Result> maybeResult = Result.flatMapMaybe(result, + maybe -> Result.of(() -> maybe.map(v -> v * 2))); +``` +--- +### Instance Methods + +#### ` Result map(Function f)` + +If the `Result` is a success, then apply the function to the value within the +`Result`, returning the result within another `Result`. If the `Result` is an +error, then return the error. + +``` java +Result result = Result.of(() -> getValue()) + .map(v -> String.valueOf(v)); +``` +--- +#### ` Result flatMap(Function> f)` + +If the `Result` is a success, then return a new `Result` containing the result +of applying the function to the contents of the `Result`. If the `Result` is an +error, then return the error. + +``` java +Result result = + Result.of(() -> getValue()) + .flatMap(v -> Result.of(() -> String.valueOf(v))); +``` +--- +#### ` Result andThen(Function> f)` + +Maps a successful `Result` to another `Result` using a `Callable` that is able +to throw a checked exception. + +``` java +Result result = + Result.of(() -> getValue()) + .andThen(v -> () -> {throw new IOException();}); +``` +--- +#### `void match(Consumer onSuccess, Consumer onError)` + +Matches the `Result`, either success or error, and supplies the appropriate +`Consumer` with the value or error. + +``` java +Result.of(() -> getValue()) + .match( + success -> System.out.println(success), + error -> System.err.println(error.getMessage()) + ); +``` +--- +#### `Result recover(Function> f)` + +Provide a way to attempt to recover from an error state. + +``` java +Result result = Result.of(() -> getValue()) + .recover(e -> Result.of(() -> getSafeValue(e))); +``` +--- +#### `Result peek(Consumer consumer)` + +Provide the value within the Result, if it is a success, to the `Consumer`, +and returns this Result. + +``` java +Result result = Result.of(() -> getValue()) + .peek(v -> System.out.println(v)); +``` +--- +#### `Result thenWith(Function> f)` + +Perform the continuation with the current `Result` value then return the +current `Result`, assuming there was no error in the continuation. + +``` java +Result result = + Result.of(() -> getValue()) + .thenWith(v -> () -> System.out.println(v)) + .thenWith(v -> () -> {throw new IOException();}); +``` +--- +#### `Result> maybe(Predicate predicate) + +Wraps the value within the `Result` in a `Maybe`, either a `Just` if the +predicate is true, or `Nothing`. + +``` java +Result> result = Result.of(() -> getValue()) + .maybe(v -> v % 2 == 0); +``` +--- +#### `T orElseThrow()` + +Extracts the successful value from the `Result`, or throws the error +within a `CheckedErrorResultException`. + +``` java +Integer result = Result.of(() -> getValue()) + .orElseThrow(); +``` +--- +#### ` T orElseThrow(Class type) throws E` + +Extracts the successful value from the `Result`, or throws the error when it +is of the given type. Any other errors will be thrown inside an +`UnexpectedErrorResultException`. + +``` java +Integer result = Result.of(() -> getValue()) + .orElseThrow(IOException.class); +``` +--- +#### `T orElseThrowUnchecked()` + +Extracts the successful value from the `Result`, or throws the error within +an `ErrorResultException`. + +``` java +Integer result = Result.of(() -> getValue()) + .orElseThrowUnchecked(); +``` +--- +#### `void onError(Consumer errorConsumer)` + +A handler for error states. If the `Result` is an error, then supply the error +to the `Consumer`. Does nothing if the `Result` is a success. + +``` java +Result.of(() -> getValue()) + .onError(e -> handleError(e)); +``` +--- +#### `boolean isOkay()` + +Checks if the `Result` is a success. + +``` java +boolean isOkay = Result.of(() -> getValue()) + .isOkay(); +``` +--- +#### `boolean isError()` + +Checks if the `Result` is an error. + +``` java +boolean isError = Result.of(() -> getValue()) + .isError(); +``` +--- +## Tree + +A Generalised tree, where each node may or may not have an item, and may have +any number of sub-trees. Leaf nodes are Trees with zero sub-trees. + +### Static Constructors + +#### `static Tree leaf(R item)` + +Create a leaf containing the item. The leaf has no sub-trees. + +``` java +Tree tree = Tree.leaf("item"); +``` +--- +#### `static Tree of(R item, Collection> subtrees)` + +Create a tree containing the item and sub-trees. + +``` java +Tree tree = Tree.of("item", Collections.singletonList(Tree.leaf("leaf")); +``` +--- +#### `static TreeBuilder builder(final Class type)` + +Create a new `TreeBuilder` starting with an empty tree. + +``` java +TreeBuilder builder = Tree.builder(Integer.class); +``` +--- +#### `static TreeBuilder builder(final Tree tree)` + +Create a new `TreeBuilder` for the given tree. + +``` java +Tree tree = ...; +TreeBuilder builder = Tree.builder(tree); +``` +--- +### Instance Methods + +#### ` Tree map(Function f)` + +Applies the function to the item within the `Tree` and to all sub-trees, +returning a new `Tree`. + +``` java +Tree tree = ...; +Tree result = tree.map(UUID::toString); +``` +--- +#### `Maybe item()` + +Returns the contents of the `Tree` node within a `Maybe`. + +``` java +Tree tree = ...; +Maybe result = tree.item(); +``` +--- +#### `int count()` + +Returns the total number of items in the `Tree`, including sub-trees. `Null` +items don't count. + +``` java +Tree tree = ...; +int result = tree.count(); +``` +--- +#### `List subTrees()` + +Returns a list of sub-trees within the `Tree`. + +``` java +Tree tree = ...; +List> result = tree.subTrees(); +``` +--- +## TreeBuilder + +A mutable builder for a `Tree`. Each `TreeBuilder` allows modification of a +single `Tree` node. You can use the `select(childItem)` method to get a +`TreeBuilder` for the subtree that has the given child item. + +### Example + +``` java +TreeBuilder builder = Tree.builder(); +builder.set(12).addChildren(Arrays.asList(1, 3, 5, 7)); +TreeBuilder builderFor3 = builder.select(3); +builderFor3.addChildren(Arrays.asList(2, 4)); +Tree tree = builder.build(); +``` + +Will produce a `Tree` like: + +![](images/treebuilder-example.svg) +--- +### Static Constructors + +None. The `TreeBuilder` is instantiated by `Tree.builder()`. + +### Instance Methods + +#### `Tree build()` + +Create the immutable Tree. + +``` java +TreeBuilder builder = Tree.builder(); +Tree tree = builder.build(); +``` +--- +#### `TreeBuilder item(T item)` + +Set the current `Tree`'s item and return the `TreeBuilder`. + +#### `TreeBuilder add(Tree subtree)` + +Adds the subtree to the current tree. + +#### `TreeBuilder addChild(T childItem)` + +Add the Child item as a sub-Tree. + +#### `TreeBuilder addChildren(List children)` + +Add all the child items as subTrees. + +#### `Maybe> select(T childItem)` + +Create a `TreeBuilder` for the subTree of the current `Tree` that has the +childItem. + +## Lazy + +A lazily evaluated expression. Using a `Supplier` to provide the value, only +evaluates the value when required, and never more than once. + +### Static Constructors + +#### `static Lazy of(Supplier supplier)` + +Create a new `Lazy` value from the `Supplier`. + +``` java +Suppler supplier = ...; +Lazy lazy = Lazy.of(supplier); +``` + +### Instance Methods + +#### `boolean isEvaluated()` + +Checks if the value has been evaluated. + +``` java +Lazy lazy = ...; +boolean isEvaluated = lazy.isEvaluated(); +``` +--- +#### `T value()` + +The value, evaluating it if necessary. + +``` java +Lazy lazy = ...; +UUID value = lazy.value(); +``` +--- +#### ` Lazy map(Function f)` + +Maps the `Lazy` instance into a new `Lazy` instance using the `Function`. + +``` java +Lazy uuidLazy = ...; +Lazy stringLazy = uuidLazy.map(v -> v.toString()); +``` +--- +## Either + +Allows handling a value that can be one of two types, a left value/type, or a +right value/type. + +`Either` *is not* a Monad. + +When an `Either` is returned from a method it will contain either a left or a +right. + +Where the `Either` is used to represent success/failure, the left case is, by +convention, used to indicate the error, and right the success. An alternative +is to use the `Result` which more clearly distinguishes success from failure. +--- +### Static Constructors + +#### `static Either left(final L l)` + +Create a new `Either` holding a left value. + +``` java +Either left = Either.left(getIntegerValue()); +``` +--- +#### `static Either right(final R r)` + +Create a new `Either` holding a right value. + +``` java +Either right = Either.right(getStringValue()); +``` +--- +### Instance Methods + +#### `boolean isLeft()` + +Checks if the `Either` holds a left value. + +``` java +Either either = Either.left(getIntegerValue()); +boolean leftIsLeft = either.isLeft(); +boolean rightIsLeft = either.isLeft(); +``` +--- +#### `boolean isRight()` + +Checks if the `Either` holds a right value. + +``` java +Either either = Either.left(getIntegerValue()); +boolean leftIsRight = either.isRight(); +boolean rightIsRight = either.isRight(); +``` +--- +#### `void match(Consumer onLeft, Consumer onRight)` + +Matches the `Either`, invoking the correct `Consumer`. + +``` java +Either either = Either.left(getIntegerValue()); +either.match( + left -> handleIntegerValue(left), + right -> handleStringValue(right) +); +``` +--- +#### ` Either mapLeft(Function f)` + +Map the `Function` across the left value. + +``` java +Either either = Either.left(getIntegerValue()); +Either either = either.mapLeft(i -> i.doubleValue()); +``` +--- +#### ` Either mapRight(Function f)` + +Map the function across the right value. + +``` java +Either either = Either.left(getIntegerValue()); +Either either = either.mapRight(s -> s + "x"); +``` +--- +#### ` Either flatMapLeft(Function> f)` + +FlatMap the function across the left value. + +``` java +Either either = Either.left(2); +Either resultLeft = either.flatMapLeft(l -> Either.left(l * 2)); +Either resultRight = either.flatMapLeft(l -> Either.right(l * 2)); +``` +--- +#### ` Either flatMapRight(Function> f)` + +FlatMap the function across the right value. + +``` java +Either either = Either.right("2"); +Either resultLeft = either.flatMapRight(l -> Either.left(l * 2)); +Either resultRight = either.flatMapRight(l -> Either.right(l * 2)); +``` +--- +#### `Optional getLeft()` + +Returns an `Optional` containing the left value, if is a left, otherwise returns +an empty `Optional`. + +``` java +Either either = Either.right("2"); +Optional left = either.getLeft(); +``` +--- +#### `Optional getRight()` + +Returns an `Optional` containing the right value, if is a right, otherwise +returns an empty `Optional`. + +``` java +Either either = Either.right("2"); +Optional right = either.getRight(); +``` + +## Combinators + +### After + +TODO + +### Before + +TODO + +### Around + +TODO diff --git a/README.org b/README.org deleted file mode 100644 index 328be6f..0000000 --- a/README.org +++ /dev/null @@ -1,1228 +0,0 @@ -* Mon -* Wrapper, TypeAlias, Maybe and Result for Java. - - [[https://oss.sonatype.org/content/repositories/releases/net/kemitix/mon][file:https://img.shields.io/nexus/r/https/oss.sonatype.org/net.kemitix/mon.svg?style=for-the-badge]] - [[https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22net.kemitix%22%20AND%20a%3A%22mon%22][file:https://img.shields.io/maven-central/v/net.kemitix/mon.svg?style=for-the-badge]] - [[http://i.jpeek.org/net.kemitix/mon/index.html][file:http://i.jpeek.org/net.kemitix/mon/badge.svg]] - - - [Maven Usage] - - [Alias] - - [TypeAlias] - - [Maybe] - - [Result] - - [Tree] - - [Lazy] - - [Either] - -* Maven Usage - -#+BEGIN_SRC xml - - net.kemitix - mon - RELEASE - -#+END_SRC - - The latest version should be shown above with the nexus and maven-central - badges or can be found on [[https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22net.kemitix%22%20AND%20a%3A%22mon%22][Maven Central]]. - -* Wrapper - - A simple FunctionalInterface that contains a value. Can be used to implement - a form of type-alias in Java. - - In Haskell it is possible to create an alias for a Type, and to then use - that alias with the same behaviour as the original, except that the compiler - doesn't treat the alias as the same Type and will generate compiler errors - if you try and use them together. e.g.: - - #+BEGIN_SRC haskell - newtype PhoneNumber = PhoneNumber String - newtype Name = Name String - newtype PhoneBookEntry = PhoneBookEntry (Name, PhoneNumber) - newtype PhoneBook = PhoneBook [PhoneBookEntry] - #+END_SRC - - In Java we don't have the ability to have that true alias, so Wrapper simply - wraps the value within a new type. It's as close as I could get to a Haskell - type alias in Java. - - The benefits of using Wrapper are: - - - encapsulation of the wrapped type when passing references through code - that doesn't need to access the actual value, but only to pass it on - - type-safe parameters where you would otherwise be passing Strings, - Integers, Lists, or other general classes - - less verbose than implementing your own - - *Wrapper Example:* - - #+BEGIN_SRC java - interface PhoneNumber extends Wrapper {} - - PhoneNumber pn = () -> "01234 567890"; - String v = pn.value(); - #+END_SRC - - *Roll your own:* - - #+BEGIN_SRC java - class PhoneNumber { - private final String value; - public PhoneNumber(final String value) { - this.value = value; - } - public String value() { - return value; - } - } - #+END_SRC - - *Lombok:* - - Using Lombok we can achieve it in 8 lines, compared to 24 for rolling your - own, or 1 for Alias: - - #+BEGIN_SRC java - @Value - @RequiredArgsConstructor(access = AccessLevel.PRIVATE) - class PhoneNumber { - private final String value; - public static PhoneNumber of(final String phoneNumber) { - return new PhoneNumber(phoneNumber); - } - } - #+END_SRC - -* TypeAlias - - Note: this is a precursor to `Wrapper` and should be considered deprecated. - - *TypeAlias Example:* - - #+BEGIN_SRC java - class PhoneNumber extends TypeAlias { - private PhoneNumber(final String value) { - super(value); - } - public static PhoneNumber of(final String phoneNumber) { - return new PhoneNumber(phoneNumber); - } - } - #+END_SRC - -** =TypeAlias= *can* be a Monad - - #+BEGIN_SRC java - package net.kemitix.mon; - - import org.assertj.core.api.WithAssertions; - import org.junit.Test; - - import java.util.function.Function; - - public class TypeAliasMonadTest implements WithAssertions { - - private final int v = 1; - private final Function> f = i -> a(i * 2); - private final Function> g = i -> a(i + 6); - - private static AnAlias a(Integer v) { - return AnAlias.of(v); - } - - @Test - public void leftIdentity() { - assertThat( - a(v).flatMap(f) - ).isEqualTo( - f.apply(v) - ); - } - - @Test - public void rightIdentity_inline() { - // java isn't able to properly infer the correct types when used in-line - assertThat( - a(v).>flatMap(x -> a(x)) - ).isEqualTo( - a(v) - ); - } - - @Test - public void rightIdentity_explicitValue() { - final AnAlias integerAnAlias = a(v).flatMap(x -> a(x)); - assertThat( - integerAnAlias - ).isEqualTo( - a(v) - ); - } - - @Test - public void associativity() { - assertThat( - a(v).flatMap(f).flatMap(g) - ).isEqualTo( - a(v).flatMap(x -> f.apply(x).flatMap(g)) - ); - } - - static class AnAlias extends TypeAlias { - private AnAlias(T value) { - super(value); - } - - static AnAlias of(T value) { - return new AnAlias<>(value); - } - } - } - #+END_SRC - - -** Instance Methods - -*** =final R map(final Function f)= - - Map the TypeAlias into another value. - - #+BEGIN_SRC java - final StudentId studentId = StudentId.of(123); - final String idString = studentId.map(id -> String.valueOf(id)); - - class StudentId extends TypeAlias { - private StudentId(Integer value) { - super(value); - } - static StudentId of(Integer id) { - return new StudentId(id); - } - } - #+END_SRC - - -*** =final > U flatMap(final Function f)= - - Map the TypeAlias into another TypeAlias. - - #+BEGIN_SRC java - final StudentId studentId = StudentId.of(123); - final StudentName studentName = studentId.flatMap(id -> getStudentName(id)); - - class StudentName extends TypeAlias { - private StudentName(String value) { - super(value); - } - static StudentName of(final String name) { - return new StudentName(name); - } - } - #+END_SRC - - -*** =T getValue()= - - Get the value of the TypeAlias. - - #+BEGIN_SRC java - final String name = studentName.getValue(); - #+END_SRC - -* Maybe - - Allows specifying that a value may or may not be present. Similar to - =Optional=. =Maybe= provides additional methods that =Optional= doesn't: - =isNothing()=, =stream()=, =ifNothing()= and =match()=. =Maybe= does not - have a =get()= method. - - Unlike =Optional=, when a =map()= results in a =null=, the =Maybe= will - continue to be a =Just=. =Optional= would switch to being empty. [[http://blog.vavr.io/the-agonizing-death-of-an-astronaut/][vavr.io - follows the same behaviour as =Maybe=]]. - - #+BEGIN_SRC java - import net.kemitix.mon.maybe.Maybe; - - import java.util.function.Function; - import java.util.function.Predicate; - - class MaybeExample { - - public static void main(String[] args) { - Maybe.just(countArgs(args)) - .filter(isEven()) - .map(validMessage()) - .match( - just -> System.out.println(just), - () -> System.out.println("Not an valid value") - ); - } - - private static Function validMessage() { - return v -> String.format("Value %d is even", v); - } - - private static Predicate isEven() { - return v -> v % 2 == 0; - } - - private static Integer countArgs(String[] args) { - return args.length; - } - } - #+END_SRC - - In the above example, the number of command line arguments are counted, if - there are an even number of them then a message is created and printed by - the Consumer parameter in the =match= call. If there is an odd number of - arguments, then the filter will return =Maybe.nothing()=, meaning that the - =nothing= drops straight through the map and triggers the Runnable parameter - in the =match= call. - -** =Maybe= is a Monad: - - #+BEGIN_SRC java - package net.kemitix.mon; - - import net.kemitix.mon.maybe.Maybe; - import org.assertj.core.api.WithAssertions; - import org.junit.Test; - - import java.util.function.Function; - - public class MaybeMonadTest implements WithAssertions { - - private final int v = 1; - private final Function> f = i -> m(i * 2); - private final Function> g = i -> m(i + 6); - - private static Maybe m(int value) { - return Maybe.maybe(value); - } - - @Test - public void leftIdentity() { - assertThat( - m(v).flatMap(f) - ).isEqualTo( - f.apply(v) - ); - } - - @Test - public void rightIdentity() { - assertThat( - m(v).flatMap(x -> m(x)) - ).isEqualTo( - m(v) - ); - } - - @Test - public void associativity() { - assertThat( - m(v).flatMap(f).flatMap(g) - ).isEqualTo( - m(v).flatMap(x -> f.apply(x).flatMap(g)) - ); - } - - } - #+END_SRC - - -** Static Constructors - -*** =static Maybe maybe(T value)= - - Create a Maybe for the value that may or may not be present. - - Where the value is =null=, that is taken as not being present. - - #+BEGIN_SRC java - final Maybe just = Maybe.maybe(1); - final Maybe nothing = Maybe.maybe(null); - #+END_SRC - - -*** =static Maybe just(T value)= - - Create a Maybe for the value that is present. - - The =value= must not be =null= or a =NullPointerException= will be thrown. - If you can't prove that the value won't be =null= you should use - =Maybe.maybe(value)= instead. - - #+BEGIN_SRC java - final Maybe just = Maybe.just(1); - #+END_SRC - - -*** =static Maybe nothing()= - - Create a Maybe for a lack of a value. - - #+BEGIN_SRC java - final Maybe nothing = Maybe.nothing(); - #+END_SRC - - -*** =static Maybe findFirst(Stream stream)= - - Creates a Maybe from the first item in the stream, or nothing if the stream is empty. - - #+BEGIN_SRC java - final Maybe just3 = Maybe.findFirst(Stream.of(3, 4, 2, 4)); - final Maybe nothing = Maybe.findFirst(Stream.empty()); - #+END_SRC - - -** Instance Methods - -*** =Maybe filter(Predicate predicate)= - - Filter a Maybe by the predicate, replacing with Nothing when it fails. - - #+BEGIN_SRC java - final Maybe maybe = Maybe.maybe(getValue()) - .filter(v -> v % 2 == 0); - #+END_SRC - - -*** = Maybe map(Function f)= - - Applies the function to the value within the Maybe, returning the result within another Maybe. - - #+BEGIN_SRC java - final Maybe maybe = Maybe.maybe(getValue()) - .map(v -> v * 100); - #+END_SRC - - -*** = Maybe flatMap(Function> f)= - - Applies the function to the value within the =Maybe=, resulting in another =Maybe=, then flattens the resulting =Maybe>= into =Maybe=. - - Monad binder maps the Maybe into another Maybe using the binder method f - - #+BEGIN_SRC java - final Maybe maybe = Maybe.maybe(getValue()) - .flatMap(v -> Maybe.maybe(getValueFor(v))); - #+END_SRC - - -*** =void match(Consumer just, Runnable nothing)= - - Matches the Maybe, either just or nothing, and performs either the - Consumer, for Just, or Runnable for nothing. - - #+BEGIN_SRC java - Maybe.maybe(getValue()) - .match( - just -> workWithValue(just), - () -> nothingToWorkWith() - ); - #+END_SRC - - -*** = R matchValue(Function justMatcher, Supplier nothingMatcher)= - - Matches the Maybe, either just or nothing, and performs either the - Function, for Just, or Supplier for nothing. - - #+BEGIN_SRC java - final String value = Maybe.maybe(getValue()) - .matchValue( - just -> Integer.toString(just), - () -> "nothing" - ); - #+END_SRC - - -*** =T orElse(T otherValue)= - - A value to use when Maybe is Nothing. - - #+BEGIN_SRC java - final Integer value = Maybe.maybe(getValue()) - .orElse(1); - #+END_SRC - - -*** =T orElseGet(Supplier otherValueSupplier)= - - Provide a value to use when Maybe is Nothing. - - #+BEGIN_SRC java - final Integer value = Maybe.maybe(getValue()) - .orElseGet(() -> getDefaultValue()); - #+END_SRC - - -*** =T or(Supplier alternative)= - - Provide an alternative Maybe to use when Maybe is Nothing. - - #+BEGIN_SRC java - final Maybe value = Maybe.maybe(getValue()) - .or(() -> Maybe.just(defaultValue)); - #+END_SRC - - -*** =void orElseThrow(Supplier error)= - - Throw the exception if the Maybe is a Nothing. - - #+BEGIN_SRC java - final Integer value = Maybe.maybe(getValue()) - .orElseThrow(() -> new RuntimeException("error")); - #+END_SRC - - -*** =Maybe peek(Consumer consumer)= - - Provide the value within the Maybe, if it exists, to the Consumer, and returns this Maybe. Conceptually equivalent to the idea of =ifPresent(...)=. - - #+BEGIN_SRC java - final Maybe maybe = Maybe.maybe(getValue()) - .peek(v -> v.foo()); - #+END_SRC - - -*** =void ifNothing(Runnable runnable)= - - Run the runnable if the Maybe is a Nothing, otherwise do nothing. - - #+BEGIN_SRC java - Maybe.maybe(getValue()) - .ifNothing(() -> doSomething()); - #+END_SRC - - -*** =Stream stream()= - - Converts the Maybe into either a single value stream or an empty stream. - - #+BEGIN_SRC java - final Stream stream = Maybe.maybe(getValue()) - .stream(); - #+END_SRC - - -*** =boolean isJust()= - - Checks if the Maybe is a Just. - - #+BEGIN_SRC java - final boolean isJust = Maybe.maybe(getValue()) - .isJust(); - #+END_SRC - - -*** =boolean isNothing()= - - Checks if the Maybe is Nothing. - - #+BEGIN_SRC java - final boolean isNothing = Maybe.maybe(getValue()) - .isNothing(); - #+END_SRC - - -*** =Optional toOptional()= - - Convert the Maybe to an Optional. - - #+BEGIN_SRC java - final Optional optional = Maybe.maybe(getValue()) - .toOptional(); - #+END_SRC - -* Result - - Allows handling error conditions without the need to catch exceptions. - - When a =Result= is returned from a method it will contain one of two values. - Either the actual result, or an error in the form of an =Exception=. The - exception is returned within the =Result= and is not thrown. - - #+BEGIN_SRC java - import net.kemitix.mon.result.Result; - - import java.io.IOException; - - class ResultExample implements Runnable { - - public static void main(final String[] args) { - new ResultExample().run(); - } - - @Override - public void run() { - Result.of(() -> callRiskyMethod()) - .flatMap(state -> doSomething(state)) - .match( - success -> System.out.println(success), - error -> error.printStackTrace() - ); - } - - private String callRiskyMethod() throws IOException { - return "I'm fine"; - } - - private Result doSomething(final String state) { - return Result.of(() -> state + ", it's all good."); - } - - } - #+END_SRC - - In the above example the string ="I'm fine"= is returned by - =callRiskyMethod()= within a successful =Result=. The =.flatMap()= call, - unwraps that =Result= and, as it is a success, passes the contents to - =doSomething()=, which in turn returns a =Result= that the =.flatMap()= call - returns. =match()= is called on the =Result= and, being a success, will call - the success =Consumer=. - - Had =callRiskyMethod()= thrown an exception it would have been caught by the - =Result.of()= method which would have then been an error =Result=. An error - Result would have ignored the =flatMap= and skipped to the =match()= when it - would have called the error =Consumer=. - -** =Result= is a Monad - - #+BEGIN_SRC java - package net.kemitix.mon; - - import net.kemitix.mon.result.Result; - import org.assertj.core.api.WithAssertions; - import org.junit.Test; - - import java.util.function.Function; - - public class ResultMonadTest implements WithAssertions { - - private final int v = 1; - private final Function> f = i -> r(i * 2); - private final Function> g = i -> r(i + 6); - - private static Result r(int v) { - return Result.ok(v); - } - - @Test - public void leftIdentity() { - assertThat( - r(v).flatMap(f) - ).isEqualTo( - f.apply(v) - ); - } - - @Test - public void rightIdentity() { - assertThat( - r(v).flatMap(x -> r(x)) - ).isEqualTo( - r(v) - ); - } - - @Test - public void associativity() { - assertThat( - r(v).flatMap(f).flatMap(g) - ).isEqualTo( - r(v).flatMap(x -> f.apply(x).flatMap(g)) - ); - } - - } - #+END_SRC - - -** Static Constructors - -*** =static Result of(Callable callable)= - - Create a Result for a output of the Callable. - - If the Callable throws and Exception, then the Result will be an error and - will contain that exception. - - This will be the main starting point for most Results where the callable - could throw an =Exception=. - - #+BEGIN_SRC java - final Result okay = Result.of(() -> 1); - final Result error = Result.of(() -> {throw new RuntimeException();}); - #+END_SRC - - -*** =static Result ok(T value)= - - Create a Result for a success. - - Use this where you have a value that you want to place into the Result context. - - #+BEGIN_SRC java - final Result okay = Result.ok(1); - #+END_SRC - - -*** =static Result error(Throwable error)= - - Create a Result for an error. - - #+BEGIN_SRC java - final Result error = Result.error(new RuntimeException()); - #+END_SRC - - -** Static Methods - - These static methods provide integration with the =Maybe= class. - - #+BEGIN_SRC java - #+END_SRC - -*** =static Maybe toMaybe(Result result)= - - Creates a =Maybe= from the =Result=, where the =Result= is a success, then - the =Maybe= will contain the value. However, if the =Result= is an error - then the =Maybe= will be nothing. - - #+BEGIN_SRC java - final Result result = Result.of(() -> getValue()); - final Maybe maybe = Result.toMaybe(result); - #+END_SRC - - -*** =static Result fromMaybe(Maybe maybe, Supplier error)= - - Creates a =Result= from the =Maybe=, where the =Result= will be an error - if the =Maybe= is nothing. Where the =Maybe= is nothing, then the - =Supplier= will provide the error for the =Result=. - - #+BEGIN_SRC java - final Maybe maybe = Maybe.maybe(getValue()); - final Result result = Result.fromMaybe(maybe, () -> new NoSuchFileException("filename")); - #+END_SRC - - -*** =static Result> invert(Maybe> maybeResult)= - - Swaps the =Result= within a =Maybe=, so that =Result= contains a =Maybe=. - - #+BEGIN_SRC java - final Maybe> maybe = Maybe.maybe(Result.of(() -> getValue())); - final Result> result = Result.invert(maybe); - #+END_SRC - - -*** =static Result> flatMapMaybe(Result> maybeResult, Function,Result>> f)= - - Applies the function to the contents of a Maybe within the Result. - - #+BEGIN_SRC java - final Result> result = Result.of(() -> Maybe.maybe(getValue())); - final Result> maybeResult = Result.flatMapMaybe(result, maybe -> Result.of(() -> maybe.map(v -> v * 2))); - #+END_SRC - - -** Instance Methods - -*** = Result map(Function f)= - - Applies the function to the value within the Functor, returning the result - within a Functor. - - #+BEGIN_SRC java - final Result result = Result.of(() -> getValue()) - .map(v -> String.valueOf(v)); - #+END_SRC - - -*** = Result flatMap(Function> f)= - - Returns a new Result consisting of the result of applying the function to - the contents of the Result. - - #+BEGIN_SRC java - final Result result = Result.of(() -> getValue()) - .flatMap(v -> Result.of(() -> String.valueOf(v))); - #+END_SRC - - -*** = Result andThen(Function> f)= - - Maps a Success Result to another Result using a Callable that is able to - throw a checked exception. - - #+BEGIN_SRC java - final Result result = Result.of(() -> getValue()) - .andThen(v -> () -> {throw new IOException();}); - #+END_SRC - - -*** =void match(Consumer onSuccess, Consumer onError)= - - Matches the Result, either success or error, and supplies the appropriate - Consumer with the value or error. - - #+BEGIN_SRC java - Result.of(() -> getValue()) - .match( - success -> System.out.println(success), - error -> System.err.println("error") - ); - #+END_SRC - - -*** =Result recover(Function> f)= - - Provide a way to attempt to recover from an error state. - - #+BEGIN_SRC java - final Result result = Result.of(() -> getValue()) - .recover(e -> Result.of(() -> getSafeValue(e))); - #+END_SRC - - -*** =Result peek(Consumer consumer)= - - Provide the value within the Result, if it is a success, to the Consumer, - and returns this Result. - - #+BEGIN_SRC java - final Result result = Result.of(() -> getValue()) - .peek(v -> System.out.println(v)); - #+END_SRC - - -*** =Result thenWith(Function> f)= - - Perform the continuation with the current Result value then return the - current Result, assuming there was no error in the continuation. - - #+BEGIN_SRC java - final Result result = Result.of(() -> getValue()) - .thenWith(v -> () -> System.out.println(v)) - .thenWith(v -> () -> {throw new IOException();}); - #+END_SRC - - -*** =Result> maybe(Predicate predicate)= - - Wraps the value within the Result in a Maybe, either a Just if the - predicate is true, or Nothing. - - #+BEGIN_SRC java - final Result> result = Result.of(() -> getValue()) - .maybe(v -> v % 2 == 0); - #+END_SRC - - -*** =T orElseThrow()= - - Extracts the successful value from the result, or throws the error - within a =CheckedErrorResultException=. - - #+BEGIN_SRC java - final Integer result = Result.of(() -> getValue()) - .orElseThrow(); - #+END_SRC - - -*** = T orElseThrow(Class type) throws E= - - Extracts the successful value from the result, or throws the error when it - is of the given type. Any other errors will be thrown inside an - =UnexpectedErrorResultException=. - - #+BEGIN_SRC java - final Integer result = Result.of(() -> getValue()) - .orElseThrow(IOException.class); - #+END_SRC - - -*** =T orElseThrowUnchecked()= - - Extracts the successful value from the result, or throws the error within - an =ErrorResultException=. - - #+BEGIN_SRC java - final Integer result = Result.of(() -> getValue()) - .orElseThrowUnchecked(); - #+END_SRC - - -*** =void onError(Consumer errorConsumer)= - - A handler for error states. - - #+BEGIN_SRC java - Result.of(() -> getValue()) - .onError(e -> handleError(e)); - #+END_SRC - - -*** =boolean isOkay()= - - Checks if the Result is a success. - - #+BEGIN_SRC java - final boolean isOkay = Result.of(() -> getValue()) - .isOkay(); - #+END_SRC - - -*** =boolean isError()= - - Checks if the Result is an error. - - #+BEGIN_SRC java - final boolean isError = Result.of(() -> getValue()) - .isError(); - #+END_SRC - -* Tree - - A Generalised tree, where each node may or may not have an item, and may have - any number of sub-trees. Leaf nodes are Trees with zero sub-trees. - -** Static Constructors - -*** =static Tree leaf(R item)= - - Create a leaf containing the item. The leaf has no sub-trees. - - #+BEGIN_SRC java - final Tree tree = Tree.leaf("item"); - #+END_SRC - - -*** =static Tree of(R item, Collection> subtrees)= - - Create a tree containing the item and sub-trees. - - #+BEGIN_SRC java - final Tree tree = Tree.of("item", Collections.singletonList(Tree.leaf("leaf")); - #+END_SRC - - -*** =static TreeBuilder builder(final Class type)= - - Create a new TreeBuilder starting with an empty tree. - - #+BEGIN_SRC java - final TreeBuilder builder = Tree.builder(Integer.class); - #+END_SRC - - -*** =static TreeBuilder builder(final Tree tree)= - - Create a new TreeBuilder for the given tree. - - #+BEGIN_SRC java - final Tree tree = ...; - final TreeBuilder builder = Tree.builder(tree); - #+END_SRC - - -** Instance Methods - -*** = Tree map(Function f)= - - Applies the function to the item within the Tree and to all sub-trees, - returning a new Tree. - - #+BEGIN_SRC java - final Tree tree = ...; - final Tree result = tree.map(UUID::toString); - #+END_SRC - - -*** =Maybe item()= - - Returns the contents of the Tree node within a Maybe. - - #+BEGIN_SRC java - final Tree tree = ...; - final Maybe result = tree.item(); - #+END_SRC - - -*** =int count()= - - Returns the total number of items in the tree, including sub-trees. Null - items don't count. - - #+BEGIN_SRC java - final Tree tree = ...; - final int result = tree.count(); - #+END_SRC - - -*** =List subTrees()= - - Returns a list of sub-trees within the tree. - - #+BEGIN_SRC java - final Tree tree = ...; - final List> result = tree.subTrees(); - #+END_SRC - -* TreeBuilder - - A mutable builder for a Tree. Each TreeBuilder allows modification of a - single Tree node. You can use the =select(childItem)= method to get a - TreeBuilder for the subtree that has the given child item. - - #+BEGIN_SRC java - final TreeBuilder builder = Tree.builder(); - builder.set(12).addChildren(Arrays.asList(1, 3, 5, 7)); - final TreeBuilder builderFor3 = builder.select(3); - builderFor3.addChildren(Arrays.asList(2, 4)); - final Tree tree = builder.build(); - #+END_SRC - - Will produce a tree like: - - [[file:images/treebuilder-example.svg]] - - -** Static Constructors - - None. The TreeBuilder is instantiated by =Tree.builder()=. - -** Instance Methods - -*** =Tree build()= - - Create the immutable Tree. - - #+BEGIN_SRC java - final TreeBuilder builder = Tree.builder(); - final Tree tree = builder.build(); - #+END_SRC - - -*** =TreeBuilder item(T item)= - - Set the current Tree's item and return the TreeBuilder. - - #+BEGIN_SRC java - #+END_SRC - - -*** =TreeBuilder add(Tree subtree)= - - Adds the subtree to the current tree. - - #+BEGIN_SRC java - #+END_SRC - - -*** =TreeBuilder addChild(T childItem)= - - Add the Child item as a sub-Tree. - - #+BEGIN_SRC java - #+END_SRC - - -*** =TreeBuilder addChildren(List children)= - - Add all the child items as subTrees. - - #+BEGIN_SRC java - #+END_SRC - - -*** =Maybe> select(T childItem)= - - Create a TreeBuilder for the subTree of the current Tree that has the - childItem. - - #+BEGIN_SRC java - #+END_SRC - -* Lazy - - A Lazy evaluated expression. Using a Supplier to provide the value, only - evaluates the value when required, and never more than once. - -** Static Constructors - -*** =static Lazy of(Supplier supplier)= - - Create a new Lazy value from the supplier. - - #+BEGIN_SRC java - final Suppler supplier = ...; - final Lazy lazy = Lazy.of(supplier); - #+END_SRC - -** Instance Methods - -*** =boolean isEvaluated()= - - Checks if the value has been evaluated. - - #+BEGIN_SRC java - final Lazy lazy = ...; - final boolean isEvaluated = lazy.isEvaluated(); - #+END_SRC - -*** =T value()= - - The value, evaluating it if necessary. - - #+BEGIN_SRC java - final Lazy lazy = ...; - final UUID value = lazy.value(); - #+END_SRC - -*** = Lazy map(Function f)= - - Maps the Lazy instance into a new Lazy instance using the function. - - #+BEGIN_SRC java - final Lazy uuidLazy = ...; - final Lazy stringLazy = uuidLazy.map(v -> v.toString()); - #+END_SRC - -* Either - - Allows handling a value that can be one of two types, a left value/type or a - right value/type. - - When an =Either= is returned from a method it will contain either a left or a - right. - - Where the =Either= is used to represent success/failure, the left case is, by - convention, used to indicate the error, and right the success. An alternative - is to use the =Result= which more clearly distinguishes success from failure. - -** =Either= *is not* a Monad. - -** Static Constructors - -*** =static Either left(final L l)= - - Create a new Either holding a left value. - - #+BEGIN_SRC java - final Either left = Either.left(getIntegerValue()); - #+END_SRC - - -*** =static Either right(final R r)= - - Create a new Either holding a right value. - - #+BEGIN_SRC java - final Either right = Either.right(getStringValue()); - #+END_SRC - - -** Instance Methods - -*** =boolean isLeft()= - - Checks if the Either holds a left value. - - #+BEGIN_SRC java - final boolean leftIsLeft = Either.left(getIntegerValue()).isLeft(); - final boolean rightIsLeft = Either.right(getStringValue()).isLeft(); - #+END_SRC - - -*** =boolean isRight()= - - Checks if the Either holds a right value. - - #+BEGIN_SRC java - final boolean leftIsRight = Either.left(getIntegerValue()).isRight(); - final boolean rightIsRight = Either.right(getStringValue()).isRight(); - #+END_SRC - - -*** =void match(Consumer onLeft, Consumer onRight)= - - Matches the Either, invoking the correct Consumer. - - #+BEGIN_SRC java - Either.left(getIntegerValue()) - .match( - left -> handleIntegerValue(left), - right -> handleStringValue(right) - ); - #+END_SRC - - -*** = Either mapLeft(Function f)= - - Map the function across the left value. - - #+BEGIN_SRC java - final Either either = Either.left(getIntegerValue()) - .mapLeft(i -> i.doubleValue()); - #+END_SRC - - -*** = Either mapRight(Function f)= - - Map the function across the right value. - - #+BEGIN_SRC java - final Either either = Either.left(getIntegerValue()) - .mapRight(s -> s + "x"); - #+END_SRC - - -*** = Either flatMapLeft(Function> f)= - - FlatMap the function across the left value. - - #+being_src java - Either either = Either.left(2); - Either resultLeft = either.flatMapLeft(l -> Either.left(l * 2)); - Either resultRight = either.flatMapLeft(l -> Either.right(l * 2)); - #+end_src - - -*** = Either flatMapRight(Function> f)= - - FlatMap the function across the right value. - - #+being_src java - Either either = Either.right("2"); - Either resultLeft = either.flatMapRight(l -> Either.left(l * 2)); - Either resultRight = either.flatMapRight(l -> Either.right(l * 2)); - #+end_src - - -*** =Optional getLeft()= - - Returns an Optional containing the left value, if is a left, otherwise returns an empty Optional. - - #+being_src java - Either either = Either.right("2"); - Optional left = either.getLeft(); - #+end_src - - -*** =Optional getRight()= - - Returns an Optional containing the right value, if is a right, otherwise returns an empty Optional. - - #+being_src java - Either either = Either.right("2"); - Optional right = either.getRight(); - #+end_src - diff --git a/pom.xml b/pom.xml index 5ec1d7a..235fd38 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ 2.3.0 Mon - TypeAlias, Result and Maybe for Java + Wrapper, TypeAlias, Result, Lazy, Maybe and combinators for Java https://github.com/kemitix/mon/issues @@ -38,9 +38,9 @@ 3.19.0 1.18.18 2.19 - 2.4.1 + 3.0.1 net.kemitix.mon - 5.0.0 + 5.5.0 1.6.4 0.12 4.1.2 @@ -105,7 +105,14 @@ true - net.kemitix.tiles:all:${kemitix-maven-tiles.version} + net.kemitix.tiles:maven-plugins:${kemitix-maven-tiles.version} + net.kemitix.tiles:compiler-jdk-lts:${kemitix-maven-tiles.version} + net.kemitix.tiles:pmd:${kemitix-maven-tiles.version} + net.kemitix.tiles:testing:${kemitix-maven-tiles.version} + net.kemitix.tiles:spotbugs:${kemitix-maven-tiles.version} + net.kemitix.tiles:coverage:${kemitix-maven-tiles.version} + net.kemitix.tiles:pitest:${kemitix-maven-tiles.version} + net.kemitix.tiles:pmd-strict:${kemitix-maven-tiles.version} net.kemitix.checkstyle:tile:${kemitix-checkstyle.version} diff --git a/src/main/java/net/kemitix/mon/Functor.java b/src/main/java/net/kemitix/mon/Functor.java index 0fd40c0..98a662b 100644 --- a/src/main/java/net/kemitix/mon/Functor.java +++ b/src/main/java/net/kemitix/mon/Functor.java @@ -48,5 +48,5 @@ public interface Functor> { * * @return a Functor containing the result of the function {@code f} applied to the value */ - public abstract F map(Function f); + F map(Function f); } diff --git a/src/main/java/net/kemitix/mon/TypeAlias.java b/src/main/java/net/kemitix/mon/TypeAlias.java index f7a22fd..4dffc0a 100644 --- a/src/main/java/net/kemitix/mon/TypeAlias.java +++ b/src/main/java/net/kemitix/mon/TypeAlias.java @@ -32,7 +32,7 @@ import java.util.function.Function; * @param the type of the alias * @author Paul Campbell (pcampbell@kemitix.net) */ -@SuppressWarnings("abstractclassname") +@SuppressWarnings({"abstractclassname", "PMD.AbstractClassWithoutAbstractMethod"}) public abstract class TypeAlias { /** diff --git a/src/main/java/net/kemitix/mon/Wrapper.java b/src/main/java/net/kemitix/mon/Wrapper.java index 8efe27f..8c886c9 100644 --- a/src/main/java/net/kemitix/mon/Wrapper.java +++ b/src/main/java/net/kemitix/mon/Wrapper.java @@ -37,6 +37,6 @@ public interface Wrapper { * * @return the value */ - public abstract T value(); + T value(); } diff --git a/src/main/java/net/kemitix/mon/combinator/After.java b/src/main/java/net/kemitix/mon/combinator/After.java index 1893a9b..8bd4179 100644 --- a/src/main/java/net/kemitix/mon/combinator/After.java +++ b/src/main/java/net/kemitix/mon/combinator/After.java @@ -52,10 +52,10 @@ public interface After extends * @return a partially applied Function that will take an argument and return the result of applying it to the * function parameter */ - public static Function decorate( + static Function decorate( final Function function, final BiConsumer after - ) { + ) { return After.create().apply(function) .apply(after); } @@ -68,7 +68,7 @@ public interface After extends * * @return a curried function that will pass the argument and the result of the function to the supplied bi-consumer */ - public static After create() { + static After create() { return function -> after -> argument -> { final R result = function.apply(argument); after.accept(argument, result); diff --git a/src/main/java/net/kemitix/mon/combinator/Around.java b/src/main/java/net/kemitix/mon/combinator/Around.java index 4da52d9..20a4232 100644 --- a/src/main/java/net/kemitix/mon/combinator/Around.java +++ b/src/main/java/net/kemitix/mon/combinator/Around.java @@ -54,10 +54,10 @@ public interface Around extends * * @return a partially applied Function that will take an argument, and the result of applying it to function */ - public static Function decorate( + static Function decorate( final Function function, final BiConsumer, T> around - ) { + ) { return Around.create().apply(function) .apply(around); } @@ -71,7 +71,7 @@ public interface Around extends * @return a curried function that will execute the around function, passing an executable and the invocations * argument. The around function must {@code execute()} the executable and may capture the result. */ - public static Around create() { + static Around create() { return function -> around -> argument -> { final AtomicReference result = new AtomicReference<>(); final Executable callback = () -> { @@ -89,13 +89,13 @@ public interface Around extends * @param the return type of the function */ @FunctionalInterface - public static interface Executable { + interface Executable { /** * Executes the function. * * @return the result of applying the function */ - public abstract R execute(); + R execute(); } } diff --git a/src/main/java/net/kemitix/mon/combinator/Before.java b/src/main/java/net/kemitix/mon/combinator/Before.java index 29780ee..4f31780 100644 --- a/src/main/java/net/kemitix/mon/combinator/Before.java +++ b/src/main/java/net/kemitix/mon/combinator/Before.java @@ -53,10 +53,10 @@ public interface Before extends * @return a partially applied Function that will take an argument and return the result of applying it to the * function parameter */ - public static Function decorate( + static Function decorate( final Consumer before, final Function function - ) { + ) { return Before.create().apply(before) .apply(function); } @@ -69,7 +69,7 @@ public interface Before extends * * @return a curried function that will pass the argument to before applying the supplied function */ - public static Before create() { + static Before create() { return before -> function -> argument -> { before.accept(argument); return function.apply(argument); diff --git a/src/main/java/net/kemitix/mon/experimental/either/Either.java b/src/main/java/net/kemitix/mon/experimental/either/Either.java index d8ca36e..06d5814 100644 --- a/src/main/java/net/kemitix/mon/experimental/either/Either.java +++ b/src/main/java/net/kemitix/mon/experimental/either/Either.java @@ -43,7 +43,7 @@ public interface Either { * @param the type of the right value * @return a Either holding the left value */ - public static Either left(final L l) { + static Either left(final L l) { return new Left<>(l); } @@ -55,7 +55,7 @@ public interface Either { * @param the type of the right value * @return a Either holding the right value */ - public static Either right(final R r) { + static Either right(final R r) { return new Right<>(r); } @@ -64,14 +64,14 @@ public interface Either { * * @return true if this Either is a left */ - public abstract boolean isLeft(); + boolean isLeft(); /** * Checks if the Either holds a right value. * * @return true if this Either is a right */ - public abstract boolean isRight(); + boolean isRight(); /** * Matches the Either, invoking the correct Consumer. @@ -79,7 +79,7 @@ public interface Either { * @param onLeft the Consumer to invoke when the Either is a left * @param onRight the Consumer to invoke when the Either is a right */ - public abstract void match(Consumer onLeft, Consumer onRight); + void match(Consumer onLeft, Consumer onRight); /** * Map the function across the left value. @@ -88,7 +88,7 @@ public interface Either { * @param the type to change the left value to * @return a new Either */ - public abstract Either mapLeft(Function f); + Either mapLeft(Function f); /** * Map the function across the right value. @@ -97,7 +97,7 @@ public interface Either { * @param the type to change the right value to * @return a new Either */ - public abstract Either mapRight(Function f); + Either mapRight(Function f); /** * FlatMap the function across the left value. @@ -106,7 +106,7 @@ public interface Either { * @param the type to change the left value to * @return a new Either if is a Left, else this */ - public abstract Either flatMapLeft(Function> f); + Either flatMapLeft(Function> f); /** * FlatMap the function across the right value. @@ -115,7 +115,7 @@ public interface Either { * @param the type to change the right value to * @return a new Either if is a Right, else this */ - public abstract Either flatMapRight(Function> f); + Either flatMapRight(Function> f); /** * Returns an Optional containing the left value, if is a left, otherwise @@ -123,7 +123,7 @@ public interface Either { * * @return An Optional containing any left value */ - public abstract Optional getLeft(); + Optional getLeft(); /** * Returns an Optional containing the right value, if is a right, otherwise @@ -131,5 +131,5 @@ public interface Either { * * @return An Optional containing any right value */ - public abstract Optional getRight(); + Optional getRight(); } diff --git a/src/main/java/net/kemitix/mon/lazy/Lazy.java b/src/main/java/net/kemitix/mon/lazy/Lazy.java index e67838f..9f00ac7 100644 --- a/src/main/java/net/kemitix/mon/lazy/Lazy.java +++ b/src/main/java/net/kemitix/mon/lazy/Lazy.java @@ -42,7 +42,7 @@ public interface Lazy extends Functor> { * @param the type of the value * @return a Lazy wrapper of the Supplier */ - public static Lazy of(final Supplier supplier) { + static Lazy of(final Supplier supplier) { return new LazySupplier<>(supplier); } @@ -51,7 +51,7 @@ public interface Lazy extends Functor> { * * @return true if the value has been evaluated. */ - public abstract boolean isEvaluated(); + boolean isEvaluated(); /** * The value, evaluating it if necessary. @@ -60,8 +60,8 @@ public interface Lazy extends Functor> { * * @return the evaluated value */ - public abstract T value(); + T value(); @Override - public abstract Lazy map(Function f); + Lazy map(Function f); } diff --git a/src/main/java/net/kemitix/mon/lazy/LazySupplier.java b/src/main/java/net/kemitix/mon/lazy/LazySupplier.java index 35f6440..15e7eec 100644 --- a/src/main/java/net/kemitix/mon/lazy/LazySupplier.java +++ b/src/main/java/net/kemitix/mon/lazy/LazySupplier.java @@ -35,10 +35,10 @@ import java.util.function.Supplier; */ class LazySupplier implements Lazy { - private final Supplier supplier; + private transient final Supplier supplier; private final AtomicBoolean evaluated = new AtomicBoolean(false); - private final AtomicReference value = new AtomicReference<>(); - private final Object lock = new Object(); + private transient final AtomicReference value = new AtomicReference<>(); + private transient final Object lock = new Object(); /** * Creates a new Lazy wrapper for the Supplier. diff --git a/src/main/java/net/kemitix/mon/maybe/Maybe.java b/src/main/java/net/kemitix/mon/maybe/Maybe.java index 233ede4..03e8ff4 100644 --- a/src/main/java/net/kemitix/mon/maybe/Maybe.java +++ b/src/main/java/net/kemitix/mon/maybe/Maybe.java @@ -50,7 +50,7 @@ public interface Maybe extends Functor> { * @param the type of the value * @return a Maybe of the value */ - public static Maybe just(@NonNull final T value) { + static Maybe just(@NonNull final T value) { return new Just<>(value); } @@ -61,7 +61,7 @@ public interface Maybe extends Functor> { * @return an empty Maybe */ @SuppressWarnings("unchecked") - public static Maybe nothing() { + static Maybe nothing() { return (Maybe) Nothing.INSTANCE; } @@ -74,7 +74,7 @@ public interface Maybe extends Functor> { * @param the type of the value * @return a Maybe, either a Just, or Nothing if value is null */ - public static Maybe maybe(final T value) { + static Maybe maybe(final T value) { if (value == null) { return nothing(); } @@ -88,7 +88,7 @@ public interface Maybe extends Functor> { * @param the type of the stream * @return a Maybe containing the first item in the stream */ - public static Maybe findFirst(Stream stream) { + static Maybe findFirst(Stream stream) { return stream.findFirst() .map(Maybe::just) .orElseGet(Maybe::nothing); @@ -99,14 +99,14 @@ public interface Maybe extends Functor> { * * @return true if the Maybe is a Just */ - public abstract boolean isJust(); + boolean isJust(); /** * Checks if the Maybe is Nothing. * * @return true if the Maybe is Nothing */ - public abstract boolean isNothing(); + boolean isNothing(); /** * Monad binder maps the Maybe into another Maybe using the binder method f. @@ -115,10 +115,10 @@ public interface Maybe extends Functor> { * @param the type of the value in the final maybe * @return a Maybe with the mapped value */ - public abstract Maybe flatMap(Function> f); + Maybe flatMap(Function> f); @Override - public abstract Maybe map(Function f); + Maybe map(Function f); /** * Provide a value to use when Maybe is Nothing. @@ -126,7 +126,7 @@ public interface Maybe extends Functor> { * @param supplier supplier for an alternate value * @return a Maybe */ - public abstract T orElseGet(Supplier supplier); + T orElseGet(Supplier supplier); /** * A value to use when Maybe is Nothing. @@ -134,14 +134,14 @@ public interface Maybe extends Functor> { * @param otherValue an alternate value * @return the value of the Maybe if a Just, otherwise the otherValue */ - public abstract T orElse(T otherValue); + T orElse(T otherValue); /** * Convert the Maybe to an {@link Optional}. * * @return an Optional containing a value for a Just, or empty for a Nothing */ - public abstract Optional toOptional(); + Optional toOptional(); /** * Throw the exception if the Maybe is a Nothing. @@ -151,14 +151,14 @@ public interface Maybe extends Functor> { * @return the value of the Maybe if a Just * @throws X if the Maybe is nothing */ - public abstract T orElseThrow(Supplier e) throws X; + T orElseThrow(Supplier e) throws X; /** * Converts the Maybe into either a single value stream or an empty stream. * * @return a Stream containing the value or nothing. */ - public abstract Stream stream(); + Stream stream(); /** * Filter a Maybe by the predicate, replacing with Nothing when it fails. @@ -166,7 +166,7 @@ public interface Maybe extends Functor> { * @param predicate the test * @return the Maybe, or Nothing if the test returns false */ - public abstract Maybe filter(Predicate predicate); + Maybe filter(Predicate predicate); /** * Provide the value within the Maybe, if it exists, to the Consumer, and returns this Maybe. @@ -174,14 +174,14 @@ public interface Maybe extends Functor> { * @param consumer the Consumer to the value if present * @return this Maybe */ - public abstract Maybe peek(Consumer consumer); + Maybe peek(Consumer consumer); /** * Run the runnable if the Maybe is a Nothing, otherwise do nothing. * * @param runnable the runnable to call if this is a Nothing */ - public abstract void ifNothing(Runnable runnable); + void ifNothing(Runnable runnable); /** * Matches the Maybe, either just or nothing, and performs either the Consumer, for Just, or Runnable for nothing. @@ -191,7 +191,7 @@ public interface Maybe extends Functor> { * @param justMatcher the Consumer to pass the value of a Just to * @param nothingMatcher the Runnable to call if the Maybe is a Nothing */ - public abstract void match(Consumer justMatcher, Runnable nothingMatcher); + void match(Consumer justMatcher, Runnable nothingMatcher); /** * Matches the Maybe, either just or nothing, and performs either the Function, for Just, or Supplier for nothing. @@ -204,7 +204,7 @@ public interface Maybe extends Functor> { * * @return the matched result, from either the justMatcher or the nothingMatcher */ - public abstract R matchValue(Function justMatcher, Supplier nothingMatcher); + R matchValue(Function justMatcher, Supplier nothingMatcher); /** * Maps the Maybe into another Maybe only when it is nothing. @@ -213,5 +213,5 @@ public interface Maybe extends Functor> { * * @return the original Maybe if not nothing, or the alternative */ - public abstract Maybe or(Supplier> alternative); + Maybe or(Supplier> alternative); } diff --git a/src/main/java/net/kemitix/mon/maybe/Nothing.java b/src/main/java/net/kemitix/mon/maybe/Nothing.java index 1af8a71..8bf6daa 100644 --- a/src/main/java/net/kemitix/mon/maybe/Nothing.java +++ b/src/main/java/net/kemitix/mon/maybe/Nothing.java @@ -50,7 +50,7 @@ final class Nothing implements Maybe { } @Override - @SuppressWarnings("unchecked") + @SuppressWarnings({"unchecked", "PMD.AvoidDuplicateLiterals"}) public Maybe flatMap(final Function> f) { return (Maybe) INSTANCE; } diff --git a/src/main/java/net/kemitix/mon/result/CheckedErrorResultException.java b/src/main/java/net/kemitix/mon/result/CheckedErrorResultException.java index 4bb06b1..1099565 100644 --- a/src/main/java/net/kemitix/mon/result/CheckedErrorResultException.java +++ b/src/main/java/net/kemitix/mon/result/CheckedErrorResultException.java @@ -30,6 +30,8 @@ package net.kemitix.mon.result; */ public final class CheckedErrorResultException extends Exception { + private static final long serialVersionUID = 6388860431020999870L; + private CheckedErrorResultException(final Throwable cause) { super(cause); } diff --git a/src/main/java/net/kemitix/mon/result/Err.java b/src/main/java/net/kemitix/mon/result/Err.java index 88d70bf..da57110 100644 --- a/src/main/java/net/kemitix/mon/result/Err.java +++ b/src/main/java/net/kemitix/mon/result/Err.java @@ -37,7 +37,7 @@ import java.util.function.Predicate; * @param the type of the value in the Result if it has been a success */ @RequiredArgsConstructor -@SuppressWarnings("methodcount") +@SuppressWarnings({"methodcount", "PMD.CyclomaticComplexity"}) class Err implements Result { private final Throwable error; diff --git a/src/main/java/net/kemitix/mon/result/ErrorResultException.java b/src/main/java/net/kemitix/mon/result/ErrorResultException.java index 7d06b2f..270682b 100644 --- a/src/main/java/net/kemitix/mon/result/ErrorResultException.java +++ b/src/main/java/net/kemitix/mon/result/ErrorResultException.java @@ -30,6 +30,8 @@ package net.kemitix.mon.result; */ public final class ErrorResultException extends RuntimeException { + private static final long serialVersionUID = -949688489831104844L; + private ErrorResultException(final Throwable cause) { super(cause); } diff --git a/src/main/java/net/kemitix/mon/result/Result.java b/src/main/java/net/kemitix/mon/result/Result.java index d5e306c..038974e 100644 --- a/src/main/java/net/kemitix/mon/result/Result.java +++ b/src/main/java/net/kemitix/mon/result/Result.java @@ -33,7 +33,7 @@ import java.util.function.*; * @param the type of the result when a success * @author Paul Campbell (pcampbell@kemitix.net) */ -@SuppressWarnings("methodcount") +@SuppressWarnings({"methodcount", "PMD.TooManyMethods"}) public interface Result extends Functor> { /** @@ -44,7 +44,7 @@ public interface Result extends Functor> { * @param the type of the Maybe and the Result * @return a Result containing the value of the Maybe when it is a Just, or the error when it is Nothing */ - public static Result fromMaybe(final Maybe maybe, final Supplier error) { + static Result fromMaybe(final Maybe maybe, final Supplier error) { return maybe.map(Result::ok) .orElseGet(() -> Result.error(error.get())); } @@ -56,7 +56,7 @@ public interface Result extends Functor> { * @param the type had the result been a success * @return an error Result */ - public default Result err(final Throwable error) { + default Result err(final Throwable error) { return new Err<>(error); } @@ -67,7 +67,7 @@ public interface Result extends Functor> { * @param the type had the result been a success * @return an error Result */ - public static Result error(final Throwable error) { + static Result error(final Throwable error) { return new Err<>(error); } @@ -78,11 +78,11 @@ public interface Result extends Functor> { * @param the type of the value * @return a Result */ - @SuppressWarnings("illegalcatch") - public default Result result(final Callable callable) { + @SuppressWarnings({"illegalcatch", "PMD.AvoidCatchingThrowable", "PMD.AvoidDuplicateLiterals"}) + default Result result(final Callable callable) { try { return Result.ok(callable.call()); - } catch (final Exception e) { + } catch (final Throwable e) { return Result.error(e); } } @@ -94,11 +94,11 @@ public interface Result extends Functor> { * @param the type of the value * @return a Result */ - @SuppressWarnings("illegalcatch") - public static Result of(final Callable callable) { + @SuppressWarnings({"illegalcatch", "PMD.AvoidCatchingThrowable"}) + static Result of(final Callable callable) { try { return Result.ok(callable.call()); - } catch (final Exception e) { + } catch (final Throwable e) { return Result.error(e); } } @@ -110,7 +110,7 @@ public interface Result extends Functor> { * @param the type of the value * @return a successful Result */ - public default Result success(final T value) { + default Result success(final T value) { return new Success<>(value); } @@ -121,7 +121,7 @@ public interface Result extends Functor> { * @param the type of the value * @return a successful Result */ - public static Result ok(final T value) { + static Result ok(final T value) { return new Success<>(value); } @@ -134,7 +134,7 @@ public interface Result extends Functor> { * @param the type of the Maybe and the Result * @return a Result containing the value of the Maybe when it is a Just, or the error when it is Nothing */ - public static Maybe toMaybe(final Result result) { + static Maybe toMaybe(final Result result) { try { return Maybe.just(result.orElseThrow()); } catch (final CheckedErrorResultException throwable) { @@ -148,7 +148,7 @@ public interface Result extends Functor> { * @return the value if a success * @throws CheckedErrorResultException if the result is an error */ - public abstract T orElseThrow() throws CheckedErrorResultException; + T orElseThrow() throws CheckedErrorResultException; /** * Extracts the successful value from the result, or throws the error Throwable. @@ -159,14 +159,14 @@ public interface Result extends Functor> { * @return the value if a success * @throws E if the result is an error */ - public abstract T orElseThrow(Class type) throws E; + T orElseThrow(Class type) throws E; /** * Extracts the successful value from the result, or throws the error in a {@link UnexpectedErrorResultException}. * * @return the value if a success */ - public abstract T orElseThrowUnchecked(); + T orElseThrowUnchecked(); /** * Swaps the inner Result of a Maybe, so that a Result is on the outside. @@ -177,7 +177,7 @@ public interface Result extends Functor> { * original Maybe. If the original Maybe is Nothing, the Result will contain Nothing. If the original Result was an * error, then the Result will also be an error. */ - public static Result> swap(final Maybe> maybeResult) { + static Result> swap(final Maybe> maybeResult) { return maybeResult.orElseGet(() -> Result.ok(null)) .flatMap(value -> Result.ok(Maybe.maybe(value))); } @@ -189,7 +189,7 @@ public interface Result extends Functor> { * @param the type of the value withing the Result of the mapping function * @return a Result */ - public abstract Result flatMap(Function> f); + Result flatMap(Function> f); /** * Applies the function to the contents of a Maybe within the Result. @@ -200,7 +200,7 @@ public interface Result extends Functor> { * @param the type of the updated Result * @return a new Maybe within a Result */ - public static Result> flatMapMaybe( + static Result> flatMapMaybe( final Result> maybeResult, final Function, Result>> f ) { @@ -212,17 +212,17 @@ public interface Result extends Functor> { * * @return true if the Result is an error. */ - public abstract boolean isError(); + boolean isError(); /** * Checks if the Result is a success. * * @return true if the Result is a success. */ - public abstract boolean isOkay(); + boolean isOkay(); @Override - public abstract Result map(Function f); + Result map(Function f); /** * Matches the Result, either success or error, and supplies the appropriate Consumer with the value or error. @@ -230,7 +230,7 @@ public interface Result extends Functor> { * @param onSuccess the Consumer to pass the value of a successful Result to * @param onError the Consumer to pass the error from an error Result to */ - public abstract void match(Consumer onSuccess, Consumer onError); + void match(Consumer onSuccess, Consumer onError); /** * Wraps the value within the Result in a Maybe, either a Just if the predicate is true, or Nothing. @@ -238,7 +238,7 @@ public interface Result extends Functor> { * @param predicate the test to decide * @return a Result containing a Maybe that may or may not contain a value */ - public abstract Result> maybe(Predicate predicate); + Result> maybe(Predicate predicate); /** * Provide the value within the Result, if it is a success, to the Consumer, and returns this Result. @@ -246,7 +246,7 @@ public interface Result extends Functor> { * @param consumer the Consumer to the value if a success * @return this Result */ - public abstract Result peek(Consumer consumer); + Result peek(Consumer consumer); /** * Provide a way to attempt to recover from an error state. @@ -254,7 +254,7 @@ public interface Result extends Functor> { * @param f the function to recover from the error * @return a new Result, either a Success, or if recovery is not possible an other Err. */ - public abstract Result recover(Function> f); + Result recover(Function> f); /** * A handler for error states. @@ -264,7 +264,7 @@ public interface Result extends Functor> { * * @param errorConsumer the consumer to handle the error */ - public abstract void onError(Consumer errorConsumer); + void onError(Consumer errorConsumer); /** * Maps a Success Result to another Result using a Callable that is able to throw a checked exception. @@ -284,7 +284,7 @@ public interface Result extends Functor> { * @param the type of the final Result * @return a new Result */ - public abstract Result andThen(Function> f); + Result andThen(Function> f); /** * Perform the continuation with the current Result value then return the current Result, assuming there was no @@ -304,7 +304,7 @@ public interface Result extends Functor> { * @param f the function to map the Success value into the result continuation * @return the Result or a new error Result */ - public abstract Result thenWith(Function> f); + Result thenWith(Function> f); /** * Reduce two Results of the same type into one using the reducing function provided. @@ -316,5 +316,5 @@ public interface Result extends Functor> { * @param operator the function to combine the values the Results * @return a Result containing the combination of the two Results */ - public abstract Result reduce(Result identify, BinaryOperator operator); + Result reduce(Result identify, BinaryOperator operator); } diff --git a/src/main/java/net/kemitix/mon/result/Success.java b/src/main/java/net/kemitix/mon/result/Success.java index cb27699..5c0a13d 100644 --- a/src/main/java/net/kemitix/mon/result/Success.java +++ b/src/main/java/net/kemitix/mon/result/Success.java @@ -37,7 +37,7 @@ import java.util.function.Predicate; * @param the type of the value in the Result */ @RequiredArgsConstructor -@SuppressWarnings("methodcount") +@SuppressWarnings({"methodcount", "PMD.CyclomaticComplexity"}) class Success implements Result { private final T value; @@ -53,8 +53,13 @@ class Success implements Result { } @Override + @SuppressWarnings({"illegalcatch", "PMD.AvoidCatchingThrowable"}) public Result map(final Function f) { - return success(f.apply(value)); + try { + return success(f.apply(this.value)); + } catch (Throwable e) { + return err(e); + } } @Override diff --git a/src/main/java/net/kemitix/mon/result/UnexpectedErrorResultException.java b/src/main/java/net/kemitix/mon/result/UnexpectedErrorResultException.java index 105e74d..5615373 100644 --- a/src/main/java/net/kemitix/mon/result/UnexpectedErrorResultException.java +++ b/src/main/java/net/kemitix/mon/result/UnexpectedErrorResultException.java @@ -31,6 +31,8 @@ package net.kemitix.mon.result; */ public final class UnexpectedErrorResultException extends RuntimeException { + private static final long serialVersionUID = 959355287011172256L; + private UnexpectedErrorResultException(final Throwable cause) { super(cause); } diff --git a/src/main/java/net/kemitix/mon/result/WithResultContinuation.java b/src/main/java/net/kemitix/mon/result/WithResultContinuation.java index 4786019..e4baa8c 100644 --- a/src/main/java/net/kemitix/mon/result/WithResultContinuation.java +++ b/src/main/java/net/kemitix/mon/result/WithResultContinuation.java @@ -35,10 +35,18 @@ public interface WithResultContinuation { * * @throws Exception to replace the current Result with an error */ - public abstract void run() throws Exception; + @SuppressWarnings("PMD.SignatureDeclareThrowsException") + void run() throws Exception; - @SuppressWarnings({"illegalcatch", "javadocmethod"}) - public default Result call(final Result currentResult) { + /** + * Internal. Called when the result of the previous {@link Result} is a + * {@link Success}. + * + * @param currentResult the previous result. + * @return the previous result if no exceptions thrown, or an {@link Err} + */ + @SuppressWarnings({"illegalcatch", "javadocmethod", "PMD.AvoidCatchingThrowable"}) + default Result call(final Result currentResult) { try { run(); } catch (Throwable e) { diff --git a/src/main/java/net/kemitix/mon/tree/GeneralisedTree.java b/src/main/java/net/kemitix/mon/tree/GeneralisedTree.java index 4d614b7..c43b5ae 100644 --- a/src/main/java/net/kemitix/mon/tree/GeneralisedTree.java +++ b/src/main/java/net/kemitix/mon/tree/GeneralisedTree.java @@ -41,8 +41,8 @@ import java.util.function.Function; @EqualsAndHashCode class GeneralisedTree implements Tree, TreeMapper { - private final T item; - private final List> subTrees; + private transient final T item; + private transient final List> subTrees; /** * Creates a new tree. diff --git a/src/main/java/net/kemitix/mon/tree/MutableTree.java b/src/main/java/net/kemitix/mon/tree/MutableTree.java index f12f4ef..da5b1b9 100644 --- a/src/main/java/net/kemitix/mon/tree/MutableTree.java +++ b/src/main/java/net/kemitix/mon/tree/MutableTree.java @@ -43,9 +43,9 @@ import java.util.stream.Collectors; @SuppressWarnings("methodcount") class MutableTree implements Tree, TreeMapper { - private final List> mySubTrees = new ArrayList<>(); + private transient final List> mySubTrees = new ArrayList<>(); - private T item; + private transient T item; /** * Create a new {@link MutableTree}. diff --git a/src/main/java/net/kemitix/mon/tree/MutableTreeBuilder.java b/src/main/java/net/kemitix/mon/tree/MutableTreeBuilder.java index 767538b..99038f4 100644 --- a/src/main/java/net/kemitix/mon/tree/MutableTreeBuilder.java +++ b/src/main/java/net/kemitix/mon/tree/MutableTreeBuilder.java @@ -35,7 +35,7 @@ import java.util.function.Function; */ class MutableTreeBuilder implements TreeBuilder { - private final MutableTree root; + private transient final MutableTree root; /** * Create empty tree builder. diff --git a/src/main/java/net/kemitix/mon/tree/Tree.java b/src/main/java/net/kemitix/mon/tree/Tree.java index fc5191d..41347db 100644 --- a/src/main/java/net/kemitix/mon/tree/Tree.java +++ b/src/main/java/net/kemitix/mon/tree/Tree.java @@ -47,7 +47,7 @@ public interface Tree extends Functor> { * * @return a empty generalised tree */ - public static Tree leaf(final R item) { + static Tree leaf(final R item) { return new GeneralisedTree<>(item, Collections.emptyList()); } @@ -59,7 +59,7 @@ public interface Tree extends Functor> { * @param the type of the item * @return a leaf node of a generalised tree */ - public static Tree of(final R item, final Collection> subtrees) { + static Tree of(final R item, final Collection> subtrees) { return new GeneralisedTree<>(item, subtrees); } @@ -73,7 +73,7 @@ public interface Tree extends Functor> { */ @SuppressFBWarnings(value = "UP_UNUSED_PARAMETER", justification = "Use the type parameter to fingerprint the return type") - public static TreeBuilder builder(final Class type) { + static TreeBuilder builder(final Class type) { return new MutableTreeBuilder<>(); } @@ -85,26 +85,26 @@ public interface Tree extends Functor> { * * @return a TreeBuilder */ - public static TreeBuilder builder(final Tree tree) { + static TreeBuilder builder(final Tree tree) { return new MutableTreeBuilder<>(MutableTree.of(tree)); } @Override - public abstract Tree map(Function f); + Tree map(Function f); /** * Return the item within the node of the tree, if present. * * @return a Maybe containing the item */ - public abstract Maybe item(); + Maybe item(); /** * Count the number of item in the tree, including subtrees. * * @return the sum of the subtrees, plus 1 if there is an item in this node */ - public default int count() { + default int count() { return item().matchValue(x -> 1, () -> 0) + subTrees().stream().mapToInt(Tree::count).sum(); } @@ -114,5 +114,5 @@ public interface Tree extends Functor> { * * @return a list of Trees */ - public abstract List> subTrees(); + List> subTrees(); } diff --git a/src/main/java/net/kemitix/mon/tree/TreeBuilder.java b/src/main/java/net/kemitix/mon/tree/TreeBuilder.java index 4006409..c343116 100644 --- a/src/main/java/net/kemitix/mon/tree/TreeBuilder.java +++ b/src/main/java/net/kemitix/mon/tree/TreeBuilder.java @@ -39,7 +39,7 @@ public interface TreeBuilder { * * @return a {@link Tree} */ - public abstract Tree build(); + Tree build(); /** * Set the current {@link Tree}'s item. @@ -48,7 +48,7 @@ public interface TreeBuilder { * * @return the TreeBuilder */ - public abstract TreeBuilder item(T item); + TreeBuilder item(T item); /** * Adds the subtree to the current tree. @@ -57,7 +57,7 @@ public interface TreeBuilder { * * @return the TreeBuilder */ - public abstract TreeBuilder add(Tree subtree); + TreeBuilder add(Tree subtree); /** * Add the Child item as a subTree. @@ -65,7 +65,7 @@ public interface TreeBuilder { * @param childItem the item to add as a subtree * @return the TreeBuilder */ - public abstract TreeBuilder addChild(T childItem); + TreeBuilder addChild(T childItem); /** * Add all the child items as subTrees. @@ -73,7 +73,7 @@ public interface TreeBuilder { * @param children the items to add as a subtree * @return the TreeBuilder */ - public default TreeBuilder addChildren(List children) { + default TreeBuilder addChildren(List children) { children.forEach(this::addChild); return this; } @@ -84,5 +84,5 @@ public interface TreeBuilder { * @param childItem the item of search the subtrees for * @return a Maybe containing the TreeBuilder for the subtree, or Nothing if there child item is not found */ - public abstract Maybe> select(T childItem); + Maybe> select(T childItem); } diff --git a/src/main/java/net/kemitix/mon/tree/TreeMapper.java b/src/main/java/net/kemitix/mon/tree/TreeMapper.java index 618a9da..cd52d95 100644 --- a/src/main/java/net/kemitix/mon/tree/TreeMapper.java +++ b/src/main/java/net/kemitix/mon/tree/TreeMapper.java @@ -43,7 +43,7 @@ interface TreeMapper { * * @return a List of mapped sub-trees */ - public default List> mapTrees( + default List> mapTrees( final Function f, final List> trees ) { diff --git a/src/test/java/net/kemitix/mon/ResultTest.java b/src/test/java/net/kemitix/mon/ResultTest.java index d793dd4..231702b 100644 --- a/src/test/java/net/kemitix/mon/ResultTest.java +++ b/src/test/java/net/kemitix/mon/ResultTest.java @@ -144,7 +144,7 @@ class ResultTest implements WithAssertions { } @Test - void okay_whenMap_isOkay() { + void okay_whenMapToOkay_isOkay() { //given final Result okResult = Result.ok(1); //when @@ -157,6 +157,22 @@ class ResultTest implements WithAssertions { ); } + @Test + void okay_whenMapToError_isError() { + //given + final Result okResult = Result.ok(1); + //when + final Result result = okResult.map(value -> { + throw new RuntimeException("map error"); + }); + //then + assertThat(result.isError()).isTrue(); + result.match( + success -> fail("not an success"), + error -> assertThat(error).hasMessage("map error") + ); + } + @Test void error_whenMap_isError() { //given