Bump kemitix-maven-tiles to 3.0.0 and kemitix-checkstye to 5.5.0 (#181)

* Bump kemitix-maven-tiles to 3.0.0

* Bump kemitix-maven-tiles to 3.0.1

* Bump kemitix-checkstyle to 5.5.0

* Docs updates - more to come

* Checkstyle and PMD compliance updates

* result: map on Success should become Error on exception

* README: reformat in markdown
This commit is contained in:
Paul Campbell 2021-03-14 15:15:50 +00:00 committed by GitHub
parent ee97d6065f
commit 5737e11395
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 1149 additions and 1341 deletions

994
README.md Normal file
View file

@ -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
<dependency>
<groupId>net.kemitix</groupId>
<artifactId>mon</artifactId>
<version>${mon.version}</version>
</dependency>
```
---
## 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<String> {}
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<String> {
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> R map(final Function<T, R> 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<Integer> {
private StudentId(Integer value) {
super(value);
}
static StudentId of(Integer id) {
return new StudentId(id);
}
}
```
---
#### `final <R, U extends TypeAlias<R>> U flatMap(final Function<T, U> f)`
Map the `TypeAlias` into another `TypeAlias`.
``` java
StudentId studentId = StudentId.of(123);
StudentName studentName = studentId.flatMap(id -> getStudentName(id));
class StudentName extends TypeAlias<String> {
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<Integer, String> validMessage() {
return v -> String.format("Value %d is even", v);
}
private static Predicate<Integer> 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 <T> Maybe<T> 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<Integer> just = Maybe.maybe(1);
Maybe<Integer> nothing = Maybe.maybe(null);
```
---
#### `static <T> Maybe<T> 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<Integer> just = Maybe.just(1);
```
---
#### `static <T> Maybe<T> nothing()`
Create a `Maybe` for a lack of a value.
``` java
Maybe<Integer> nothing = Maybe.nothing();
```
---
#### `static <T> Maybe<T> findFirst(Stream<T> stream)`
Creates a `Maybe` from the first item in the stream, or nothing if the stream
is empty.
``` java
Maybe<Integer> just3 = Maybe.findFirst(Stream.of(3, 4, 2, 4));
Maybe<Integer> nothing = Maybe.findFirst(Stream.empty());
```
---
### Instance Methods
#### `Maybe<T> filter(Predicate<T> predicate)`
Filter a Maybe by the predicate, replacing with Nothing when it fails.
``` java
Maybe<Integer> maybe = Maybe.maybe(getValue())
.filter(v -> v % 2 == 0);
```
---
#### `<R> Maybe<R> map(Function<T,R> f)`
Applies the function to the value within the `Maybe`, returning the result
within another `Maybe`.
``` java
Maybe<Integer> maybe = Maybe.maybe(getValue())
.map(v -> v * 100);
```
---
#### `<R> Maybe<R> flatMap(Function<T,Maybe<R>> f)`
Applies the function to the value within the `Maybe`, resulting in another
`Maybe`, then flattens the resulting `Maybe<Maybe<T>>` into `Maybe<T>`.
``` java
Maybe<Integer> maybe = Maybe.maybe(getValue())
.flatMap(v -> Maybe.maybe(getValueFor(v)));
```
---
#### `void match(Consumer<T> 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> R matchValue(Function<T, R> justMatcher, Supplier<R> 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<T> otherValueSupplier)`
Provide a value to use when the `Maybe` is `Nothing`.
``` java
Integer value = Maybe.maybe(getValue())
.orElseGet(() -> getDefaultValue());
```
---
#### `T or(Supplier<Maybe<T> alternative)`
Provide an alternative `Maybe` to use when the `Maybe` is `Nothing`.
``` java
Maybe<Integer> value = Maybe.maybe(getValue())
.or(() -> Maybe.just(defaultValue));
```
---
#### `void orElseThrow(Supplier<Exception> error)`
Throw the exception if the `Maybe` is `Nothing`.
``` java
Integer value = Maybe.maybe(getValue())
.orElseThrow(() -> new RuntimeException("error"));
```
---
#### `Maybe<T> peek(Consumer<T> consumer)`
Provide the value within the `Maybe`, if it exists, to the `Consumer`, and
returns the original `Maybe`.
``` java
Maybe<Integer> 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<T> stream()`
Converts the `Maybe` into either a single value stream or an empty stream.
``` java
Stream<Integer> 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<T> toOptional()`
Convert the `Maybe` to an `Optional`.
``` java
Optional<Integer> 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<String> 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 <T> Result<T> of(Callable<T> 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<Integer> okay = Result.of(() -> 1);
Result<Integer> error = Result.of(() -> {throw new RuntimeException();});
```
---
#### `static <T> Result<T> 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<Integer> okay = Result.ok(1);
```
---
#### `static <T> Result<T> error(Throwable error)`
Create a `Result` for an error.
``` java
Result<Integer> error = Result.error(new RuntimeException());
```
---
### Static Methods
These static methods provide integration with the `Maybe` class.
#### `static <T> Maybe<T> toMaybe(Result<T> 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<Integer> result = Result.of(() -> getValue());
Maybe<Integer> maybe = Result.toMaybe(result);
```
---
#### `static <T> Result<T> fromMaybe(Maybe<T> maybe, Supplier<Throwable> 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<Throwable>` will provide the error for the `Result`.
``` java
Maybe<Integer> maybe = Maybe.maybe(getValue());
Result<Integer> result = Result.fromMaybe(maybe,
() -> new NoSuchFileException("filename"));
```
---
#### `static <T> Result<Maybe<T>> invert(Maybe<Result<T>> maybeResult)`
Swaps the `Result` within a `Maybe`, so that `Result` contains a `Maybe`.
``` java
Maybe<Result<Integer>> maybe = Maybe.maybe(Result.of(() -> getValue()));
Result<Maybe<Integer>> result = Result.invert(maybe);
```
---
#### `static <T,R> Result<Maybe<R>> flatMapMaybe(Result<Maybe<T>> maybeResult, Function<Maybe<T>,Result<Maybe<R>>> f)`
Applies the function to the contents of a `Maybe` within the `Result`.
``` java
Result<Maybe<Integer>> result = Result.of(() -> Maybe.maybe(getValue()));
Result<Maybe<Integer>> maybeResult = Result.flatMapMaybe(result,
maybe -> Result.of(() -> maybe.map(v -> v * 2)));
```
---
### Instance Methods
#### `<R> Result<R> map(Function<T,R> 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<String> result = Result.of(() -> getValue())
.map(v -> String.valueOf(v));
```
---
#### `<R> Result<R> flatMap(Function<T,Result<R>> 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<String> result =
Result.of(() -> getValue())
.flatMap(v -> Result.of(() -> String.valueOf(v)));
```
---
#### `<R> Result<R> andThen(Function<T,Callable<R>> f)`
Maps a successful `Result` to another `Result` using a `Callable` that is able
to throw a checked exception.
``` java
Result<String> result =
Result.of(() -> getValue())
.andThen(v -> () -> {throw new IOException();});
```
---
#### `void match(Consumer<T> onSuccess, Consumer<Throwable> 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<T> recover(Function<Throwable,Result<T>> f)`
Provide a way to attempt to recover from an error state.
``` java
Result<Integer> result = Result.of(() -> getValue())
.recover(e -> Result.of(() -> getSafeValue(e)));
```
---
#### `Result<T> peek(Consumer<T> consumer)`
Provide the value within the Result, if it is a success, to the `Consumer`,
and returns this Result.
``` java
Result<Integer> result = Result.of(() -> getValue())
.peek(v -> System.out.println(v));
```
---
#### `Result<T> thenWith(Function<T,WithResultContinuation<T>> f)`
Perform the continuation with the current `Result` value then return the
current `Result`, assuming there was no error in the continuation.
``` java
Result<Integer> result =
Result.of(() -> getValue())
.thenWith(v -> () -> System.out.println(v))
.thenWith(v -> () -> {throw new IOException();});
```
---
#### `Result<Maybe<T>> maybe(Predicate<T> predicate)
Wraps the value within the `Result` in a `Maybe`, either a `Just` if the
predicate is true, or `Nothing`.
``` java
Result<Maybe<Integer>> 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();
```
---
#### `<E extends Exception> T orElseThrow(Class<E> 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<Throwable> 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 <R> Tree<R> leaf(R item)`
Create a leaf containing the item. The leaf has no sub-trees.
``` java
Tree<String> tree = Tree.leaf("item");
```
---
#### `static<R> Tree<R> of(R item, Collection<Tree<R>> subtrees)`
Create a tree containing the item and sub-trees.
``` java
Tree<String> tree = Tree.of("item", Collections.singletonList(Tree.leaf("leaf"));
```
---
#### `static <B> TreeBuilder<B> builder(final Class<B> type)`
Create a new `TreeBuilder` starting with an empty tree.
``` java
TreeBuilder<Integer> builder = Tree.builder(Integer.class);
```
---
#### `static <B> TreeBuilder<B> builder(final Tree<B> tree)`
Create a new `TreeBuilder` for the given tree.
``` java
Tree<Integer> tree = ...;
TreeBuilder<Integer> builder = Tree.builder(tree);
```
---
### Instance Methods
#### `<R> Tree<R> map(Function<T, R> f)`
Applies the function to the item within the `Tree` and to all sub-trees,
returning a new `Tree`.
``` java
Tree<UUID> tree = ...;
Tree<String> result = tree.map(UUID::toString);
```
---
#### `Maybe<T> item()`
Returns the contents of the `Tree` node within a `Maybe`.
``` java
Tree<Item> tree = ...;
Maybe<Item> result = tree.item();
```
---
#### `int count()`
Returns the total number of items in the `Tree`, including sub-trees. `Null`
items don't count.
``` java
Tree<Item> tree = ...;
int result = tree.count();
```
---
#### `List<Tree<T> subTrees()`
Returns a list of sub-trees within the `Tree`.
``` java
Tree<Item> tree = ...;
List<Tree<Item>> 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<Integer> builder = Tree.builder();
builder.set(12).addChildren(Arrays.asList(1, 3, 5, 7));
TreeBuilder<Integer> builderFor3 = builder.select(3);
builderFor3.addChildren(Arrays.asList(2, 4));
Tree<Integer> 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<T> build()`
Create the immutable Tree.
``` java
TreeBuilder<Integer> builder = Tree.builder();
Tree<Integer> tree = builder.build();
```
---
#### `TreeBuilder<T> item(T item)`
Set the current `Tree`'s item and return the `TreeBuilder`.
#### `TreeBuilder<T> add(Tree<T> subtree)`
Adds the subtree to the current tree.
#### `TreeBuilder<T> addChild(T childItem)`
Add the Child item as a sub-Tree.
#### `TreeBuilder<T> addChildren(List<T> children)`
Add all the child items as subTrees.
#### `Maybe<TreeBuilder<T>> 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 <R> Lazy<R> of(Supplier<R> supplier)`
Create a new `Lazy` value from the `Supplier`.
``` java
Suppler<UUID> supplier = ...;
Lazy<UUID> lazy = Lazy.of(supplier);
```
### Instance Methods
#### `boolean isEvaluated()`
Checks if the value has been evaluated.
``` java
Lazy<UUID> lazy = ...;
boolean isEvaluated = lazy.isEvaluated();
```
---
#### `T value()`
The value, evaluating it if necessary.
``` java
Lazy<UUID> lazy = ...;
UUID value = lazy.value();
```
---
#### `<R> Lazy<R> map(Function<T, R> f)`
Maps the `Lazy` instance into a new `Lazy` instance using the `Function`.
``` java
Lazy<UUID> uuidLazy = ...;
Lazy<String> 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 <L, R> Either<L, R> left(final L l)`
Create a new `Either` holding a left value.
``` java
Either<Integer, String> left = Either.left(getIntegerValue());
```
---
#### `static <L, R> Either<L, R> right(final R r)`
Create a new `Either` holding a right value.
``` java
Either<Integer, String> right = Either.right(getStringValue());
```
---
### Instance Methods
#### `boolean isLeft()`
Checks if the `Either` holds a left value.
``` java
Either<Integer, String> either = Either.left(getIntegerValue());
boolean leftIsLeft = either.isLeft();
boolean rightIsLeft = either.isLeft();
```
---
#### `boolean isRight()`
Checks if the `Either` holds a right value.
``` java
Either<Integer, String> either = Either.left(getIntegerValue());
boolean leftIsRight = either.isRight();
boolean rightIsRight = either.isRight();
```
---
#### `void match(Consumer<L> onLeft, Consumer<R> onRight)`
Matches the `Either`, invoking the correct `Consumer`.
``` java
Either<Integer, String> either = Either.left(getIntegerValue());
either.match(
left -> handleIntegerValue(left),
right -> handleStringValue(right)
);
```
---
#### `<T> Either<T, R> mapLeft(Function<L, T> f)`
Map the `Function` across the left value.
``` java
Either<Integer, String> either = Either.left(getIntegerValue());
Either<Double, String> either = either.mapLeft(i -> i.doubleValue());
```
---
#### `<T> Either<L, T> mapRight(Function<R, T> f)`
Map the function across the right value.
``` java
Either<Integer, String> either = Either.left(getIntegerValue());
Either<Integer, String> either = either.mapRight(s -> s + "x");
```
---
#### `<T> Either<T, R> flatMapLeft(Function<L, Either<T, R>> f)`
FlatMap the function across the left value.
``` java
Either<Integer, String> either = Either.left(2);
Either<Integer, String> resultLeft = either.flatMapLeft(l -> Either.left(l * 2));
Either<Integer, String> resultRight = either.flatMapLeft(l -> Either.right(l * 2));
```
---
#### `<T> Either<T, R> flatMapRight(Function<L, Either<T, R>> f)`
FlatMap the function across the right value.
``` java
Either<Integer, String> either = Either.right("2");
Either<Integer, String> resultLeft = either.flatMapRight(l -> Either.left(l * 2));
Either<Integer, String> resultRight = either.flatMapRight(l -> Either.right(l * 2));
```
---
#### `Optional<L> getLeft()`
Returns an `Optional` containing the left value, if is a left, otherwise returns
an empty `Optional`.
``` java
Either<Integer, String> either = Either.right("2");
Optional<Integer> left = either.getLeft();
```
---
#### `Optional<R> getRight()`
Returns an `Optional` containing the right value, if is a right, otherwise
returns an empty `Optional`.
``` java
Either<Integer, String> either = Either.right("2");
Optional<String> right = either.getRight();
```
## Combinators
### After
TODO
### Before
TODO
### Around
TODO

1228
README.org

File diff suppressed because it is too large Load diff

15
pom.xml
View file

@ -14,7 +14,7 @@
<version>2.3.0</version> <version>2.3.0</version>
<name>Mon</name> <name>Mon</name>
<description>TypeAlias, Result and Maybe for Java</description> <description>Wrapper, TypeAlias, Result, Lazy, Maybe and combinators for Java</description>
<issueManagement> <issueManagement>
<url>https://github.com/kemitix/mon/issues</url> <url>https://github.com/kemitix/mon/issues</url>
@ -38,9 +38,9 @@
<assertj.version>3.19.0</assertj.version> <assertj.version>3.19.0</assertj.version>
<lombok.version>1.18.18</lombok.version> <lombok.version>1.18.18</lombok.version>
<tiles-maven-plugin.version>2.19</tiles-maven-plugin.version> <tiles-maven-plugin.version>2.19</tiles-maven-plugin.version>
<kemitix-maven-tiles.version>2.4.1</kemitix-maven-tiles.version> <kemitix-maven-tiles.version>3.0.1</kemitix-maven-tiles.version>
<digraph-dependency.basePackage>net.kemitix.mon</digraph-dependency.basePackage> <digraph-dependency.basePackage>net.kemitix.mon</digraph-dependency.basePackage>
<kemitix-checkstyle.version>5.0.0</kemitix-checkstyle.version> <kemitix-checkstyle.version>5.5.0</kemitix-checkstyle.version>
<pitest-maven-plugin.version>1.6.4</pitest-maven-plugin.version> <pitest-maven-plugin.version>1.6.4</pitest-maven-plugin.version>
<pitest-junit5-plugin.version>0.12</pitest-junit5-plugin.version> <pitest-junit5-plugin.version>0.12</pitest-junit5-plugin.version>
<spotbugs.version>4.1.2</spotbugs.version> <spotbugs.version>4.1.2</spotbugs.version>
@ -105,7 +105,14 @@
<extensions>true</extensions> <extensions>true</extensions>
<configuration> <configuration>
<tiles> <tiles>
<tile>net.kemitix.tiles:all:${kemitix-maven-tiles.version}</tile> <tile>net.kemitix.tiles:maven-plugins:${kemitix-maven-tiles.version}</tile>
<tile>net.kemitix.tiles:compiler-jdk-lts:${kemitix-maven-tiles.version}</tile>
<tile>net.kemitix.tiles:pmd:${kemitix-maven-tiles.version}</tile>
<tile>net.kemitix.tiles:testing:${kemitix-maven-tiles.version}</tile>
<tile>net.kemitix.tiles:spotbugs:${kemitix-maven-tiles.version}</tile>
<tile>net.kemitix.tiles:coverage:${kemitix-maven-tiles.version}</tile>
<tile>net.kemitix.tiles:pitest:${kemitix-maven-tiles.version}</tile>
<tile>net.kemitix.tiles:pmd-strict:${kemitix-maven-tiles.version}</tile>
<tile>net.kemitix.checkstyle:tile:${kemitix-checkstyle.version}</tile> <tile>net.kemitix.checkstyle:tile:${kemitix-checkstyle.version}</tile>
</tiles> </tiles>
</configuration> </configuration>

View file

@ -48,5 +48,5 @@ public interface Functor<T, F extends Functor<?, ?>> {
* *
* @return a Functor containing the result of the function {@code f} applied to the value * @return a Functor containing the result of the function {@code f} applied to the value
*/ */
public abstract <R> F map(Function<T, R> f); <R> F map(Function<T, R> f);
} }

View file

@ -32,7 +32,7 @@ import java.util.function.Function;
* @param <T> the type of the alias * @param <T> the type of the alias
* @author Paul Campbell (pcampbell@kemitix.net) * @author Paul Campbell (pcampbell@kemitix.net)
*/ */
@SuppressWarnings("abstractclassname") @SuppressWarnings({"abstractclassname", "PMD.AbstractClassWithoutAbstractMethod"})
public abstract class TypeAlias<T> { public abstract class TypeAlias<T> {
/** /**

View file

@ -37,6 +37,6 @@ public interface Wrapper<T> {
* *
* @return the value * @return the value
*/ */
public abstract T value(); T value();
} }

View file

@ -52,7 +52,7 @@ public interface After<T, R> extends
* @return a partially applied Function that will take an argument and return the result of applying it to the * @return a partially applied Function that will take an argument and return the result of applying it to the
* function parameter * function parameter
*/ */
public static <T, R> Function<T, R> decorate( static <T, R> Function<T, R> decorate(
final Function<T, R> function, final Function<T, R> function,
final BiConsumer<T, R> after final BiConsumer<T, R> after
) { ) {
@ -68,7 +68,7 @@ public interface After<T, R> extends
* *
* @return a curried function that will pass the argument and the result of the function to the supplied bi-consumer * @return a curried function that will pass the argument and the result of the function to the supplied bi-consumer
*/ */
public static <T, R> After<T, R> create() { static <T, R> After<T, R> create() {
return function -> after -> argument -> { return function -> after -> argument -> {
final R result = function.apply(argument); final R result = function.apply(argument);
after.accept(argument, result); after.accept(argument, result);

View file

@ -54,7 +54,7 @@ public interface Around<T, R> extends
* *
* @return a partially applied Function that will take an argument, and the result of applying it to function * @return a partially applied Function that will take an argument, and the result of applying it to function
*/ */
public static <T, R> Function<T, R> decorate( static <T, R> Function<T, R> decorate(
final Function<T, R> function, final Function<T, R> function,
final BiConsumer<Executable<R>, T> around final BiConsumer<Executable<R>, T> around
) { ) {
@ -71,7 +71,7 @@ public interface Around<T, R> extends
* @return a curried function that will execute the around function, passing an executable and the invocations * @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. * argument. The around function must {@code execute()} the executable and may capture the result.
*/ */
public static <T, R> Around<T, R> create() { static <T, R> Around<T, R> create() {
return function -> around -> argument -> { return function -> around -> argument -> {
final AtomicReference<R> result = new AtomicReference<>(); final AtomicReference<R> result = new AtomicReference<>();
final Executable<R> callback = () -> { final Executable<R> callback = () -> {
@ -89,13 +89,13 @@ public interface Around<T, R> extends
* @param <R> the return type of the function * @param <R> the return type of the function
*/ */
@FunctionalInterface @FunctionalInterface
public static interface Executable<R> { interface Executable<R> {
/** /**
* Executes the function. * Executes the function.
* *
* @return the result of applying the function * @return the result of applying the function
*/ */
public abstract R execute(); R execute();
} }
} }

View file

@ -53,7 +53,7 @@ public interface Before<T, R> extends
* @return a partially applied Function that will take an argument and return the result of applying it to the * @return a partially applied Function that will take an argument and return the result of applying it to the
* function parameter * function parameter
*/ */
public static <T, R> Function<T, R> decorate( static <T, R> Function<T, R> decorate(
final Consumer<T> before, final Consumer<T> before,
final Function<T, R> function final Function<T, R> function
) { ) {
@ -69,7 +69,7 @@ public interface Before<T, R> extends
* *
* @return a curried function that will pass the argument to before applying the supplied function * @return a curried function that will pass the argument to before applying the supplied function
*/ */
public static <T, R> Before<T, R> create() { static <T, R> Before<T, R> create() {
return before -> function -> argument -> { return before -> function -> argument -> {
before.accept(argument); before.accept(argument);
return function.apply(argument); return function.apply(argument);

View file

@ -43,7 +43,7 @@ public interface Either<L, R> {
* @param <R> the type of the right value * @param <R> the type of the right value
* @return a Either holding the left value * @return a Either holding the left value
*/ */
public static <L, R> Either<L, R> left(final L l) { static <L, R> Either<L, R> left(final L l) {
return new Left<>(l); return new Left<>(l);
} }
@ -55,7 +55,7 @@ public interface Either<L, R> {
* @param <R> the type of the right value * @param <R> the type of the right value
* @return a Either holding the right value * @return a Either holding the right value
*/ */
public static <L, R> Either<L, R> right(final R r) { static <L, R> Either<L, R> right(final R r) {
return new Right<>(r); return new Right<>(r);
} }
@ -64,14 +64,14 @@ public interface Either<L, R> {
* *
* @return true if this Either is a left * @return true if this Either is a left
*/ */
public abstract boolean isLeft(); boolean isLeft();
/** /**
* Checks if the Either holds a right value. * Checks if the Either holds a right value.
* *
* @return true if this Either is a right * @return true if this Either is a right
*/ */
public abstract boolean isRight(); boolean isRight();
/** /**
* Matches the Either, invoking the correct Consumer. * Matches the Either, invoking the correct Consumer.
@ -79,7 +79,7 @@ public interface Either<L, R> {
* @param onLeft the Consumer to invoke when the Either is a left * @param onLeft the Consumer to invoke when the Either is a left
* @param onRight the Consumer to invoke when the Either is a right * @param onRight the Consumer to invoke when the Either is a right
*/ */
public abstract void match(Consumer<L> onLeft, Consumer<R> onRight); void match(Consumer<L> onLeft, Consumer<R> onRight);
/** /**
* Map the function across the left value. * Map the function across the left value.
@ -88,7 +88,7 @@ public interface Either<L, R> {
* @param <T> the type to change the left value to * @param <T> the type to change the left value to
* @return a new Either * @return a new Either
*/ */
public abstract <T> Either<T, R> mapLeft(Function<L, T> f); <T> Either<T, R> mapLeft(Function<L, T> f);
/** /**
* Map the function across the right value. * Map the function across the right value.
@ -97,7 +97,7 @@ public interface Either<L, R> {
* @param <T> the type to change the right value to * @param <T> the type to change the right value to
* @return a new Either * @return a new Either
*/ */
public abstract <T> Either<L, T> mapRight(Function<R, T> f); <T> Either<L, T> mapRight(Function<R, T> f);
/** /**
* FlatMap the function across the left value. * FlatMap the function across the left value.
@ -106,7 +106,7 @@ public interface Either<L, R> {
* @param <T> the type to change the left value to * @param <T> the type to change the left value to
* @return a new Either if is a Left, else this * @return a new Either if is a Left, else this
*/ */
public abstract <T> Either<T, R> flatMapLeft(Function<L, Either<T, R>> f); <T> Either<T, R> flatMapLeft(Function<L, Either<T, R>> f);
/** /**
* FlatMap the function across the right value. * FlatMap the function across the right value.
@ -115,7 +115,7 @@ public interface Either<L, R> {
* @param <T> the type to change the right value to * @param <T> the type to change the right value to
* @return a new Either if is a Right, else this * @return a new Either if is a Right, else this
*/ */
public abstract <T> Either<L, T> flatMapRight(Function<R, Either<L, T>> f); <T> Either<L, T> flatMapRight(Function<R, Either<L, T>> f);
/** /**
* Returns an Optional containing the left value, if is a left, otherwise * Returns an Optional containing the left value, if is a left, otherwise
@ -123,7 +123,7 @@ public interface Either<L, R> {
* *
* @return An Optional containing any left value * @return An Optional containing any left value
*/ */
public abstract Optional<L> getLeft(); Optional<L> getLeft();
/** /**
* Returns an Optional containing the right value, if is a right, otherwise * Returns an Optional containing the right value, if is a right, otherwise
@ -131,5 +131,5 @@ public interface Either<L, R> {
* *
* @return An Optional containing any right value * @return An Optional containing any right value
*/ */
public abstract Optional<R> getRight(); Optional<R> getRight();
} }

View file

@ -42,7 +42,7 @@ public interface Lazy<T> extends Functor<T, Lazy<?>> {
* @param <R> the type of the value * @param <R> the type of the value
* @return a Lazy wrapper of the Supplier * @return a Lazy wrapper of the Supplier
*/ */
public static <R> Lazy<R> of(final Supplier<R> supplier) { static <R> Lazy<R> of(final Supplier<R> supplier) {
return new LazySupplier<>(supplier); return new LazySupplier<>(supplier);
} }
@ -51,7 +51,7 @@ public interface Lazy<T> extends Functor<T, Lazy<?>> {
* *
* @return true if the value has been evaluated. * @return true if the value has been evaluated.
*/ */
public abstract boolean isEvaluated(); boolean isEvaluated();
/** /**
* The value, evaluating it if necessary. * The value, evaluating it if necessary.
@ -60,8 +60,8 @@ public interface Lazy<T> extends Functor<T, Lazy<?>> {
* *
* @return the evaluated value * @return the evaluated value
*/ */
public abstract T value(); T value();
@Override @Override
public abstract <R> Lazy<R> map(Function<T, R> f); <R> Lazy<R> map(Function<T, R> f);
} }

View file

@ -35,10 +35,10 @@ import java.util.function.Supplier;
*/ */
class LazySupplier<T> implements Lazy<T> { class LazySupplier<T> implements Lazy<T> {
private final Supplier<T> supplier; private transient final Supplier<T> supplier;
private final AtomicBoolean evaluated = new AtomicBoolean(false); private final AtomicBoolean evaluated = new AtomicBoolean(false);
private final AtomicReference<T> value = new AtomicReference<>(); private transient final AtomicReference<T> value = new AtomicReference<>();
private final Object lock = new Object(); private transient final Object lock = new Object();
/** /**
* Creates a new Lazy wrapper for the Supplier. * Creates a new Lazy wrapper for the Supplier.

View file

@ -50,7 +50,7 @@ public interface Maybe<T> extends Functor<T, Maybe<?>> {
* @param <T> the type of the value * @param <T> the type of the value
* @return a Maybe of the value * @return a Maybe of the value
*/ */
public static <T> Maybe<T> just(@NonNull final T value) { static <T> Maybe<T> just(@NonNull final T value) {
return new Just<>(value); return new Just<>(value);
} }
@ -61,7 +61,7 @@ public interface Maybe<T> extends Functor<T, Maybe<?>> {
* @return an empty Maybe * @return an empty Maybe
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static <T> Maybe<T> nothing() { static <T> Maybe<T> nothing() {
return (Maybe<T>) Nothing.INSTANCE; return (Maybe<T>) Nothing.INSTANCE;
} }
@ -74,7 +74,7 @@ public interface Maybe<T> extends Functor<T, Maybe<?>> {
* @param <T> the type of the value * @param <T> the type of the value
* @return a Maybe, either a Just, or Nothing if value is null * @return a Maybe, either a Just, or Nothing if value is null
*/ */
public static <T> Maybe<T> maybe(final T value) { static <T> Maybe<T> maybe(final T value) {
if (value == null) { if (value == null) {
return nothing(); return nothing();
} }
@ -88,7 +88,7 @@ public interface Maybe<T> extends Functor<T, Maybe<?>> {
* @param <T> the type of the stream * @param <T> the type of the stream
* @return a Maybe containing the first item in the stream * @return a Maybe containing the first item in the stream
*/ */
public static <T> Maybe<T> findFirst(Stream<T> stream) { static <T> Maybe<T> findFirst(Stream<T> stream) {
return stream.findFirst() return stream.findFirst()
.map(Maybe::just) .map(Maybe::just)
.orElseGet(Maybe::nothing); .orElseGet(Maybe::nothing);
@ -99,14 +99,14 @@ public interface Maybe<T> extends Functor<T, Maybe<?>> {
* *
* @return true if the Maybe is a Just * @return true if the Maybe is a Just
*/ */
public abstract boolean isJust(); boolean isJust();
/** /**
* Checks if the Maybe is Nothing. * Checks if the Maybe is Nothing.
* *
* @return true 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. * Monad binder maps the Maybe into another Maybe using the binder method f.
@ -115,10 +115,10 @@ public interface Maybe<T> extends Functor<T, Maybe<?>> {
* @param <R> the type of the value in the final maybe * @param <R> the type of the value in the final maybe
* @return a Maybe with the mapped value * @return a Maybe with the mapped value
*/ */
public abstract <R> Maybe<R> flatMap(Function<T, Maybe<R>> f); <R> Maybe<R> flatMap(Function<T, Maybe<R>> f);
@Override @Override
public abstract <R> Maybe<R> map(Function<T, R> f); <R> Maybe<R> map(Function<T, R> f);
/** /**
* Provide a value to use when Maybe is Nothing. * Provide a value to use when Maybe is Nothing.
@ -126,7 +126,7 @@ public interface Maybe<T> extends Functor<T, Maybe<?>> {
* @param supplier supplier for an alternate value * @param supplier supplier for an alternate value
* @return a Maybe * @return a Maybe
*/ */
public abstract T orElseGet(Supplier<T> supplier); T orElseGet(Supplier<T> supplier);
/** /**
* A value to use when Maybe is Nothing. * A value to use when Maybe is Nothing.
@ -134,14 +134,14 @@ public interface Maybe<T> extends Functor<T, Maybe<?>> {
* @param otherValue an alternate value * @param otherValue an alternate value
* @return the value of the Maybe if a Just, otherwise the otherValue * @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}. * Convert the Maybe to an {@link Optional}.
* *
* @return an Optional containing a value for a Just, or empty for a Nothing * @return an Optional containing a value for a Just, or empty for a Nothing
*/ */
public abstract Optional<T> toOptional(); Optional<T> toOptional();
/** /**
* Throw the exception if the Maybe is a Nothing. * Throw the exception if the Maybe is a Nothing.
@ -151,14 +151,14 @@ public interface Maybe<T> extends Functor<T, Maybe<?>> {
* @return the value of the Maybe if a Just * @return the value of the Maybe if a Just
* @throws X if the Maybe is nothing * @throws X if the Maybe is nothing
*/ */
public abstract <X extends Throwable> T orElseThrow(Supplier<? extends X> e) throws X; <X extends Throwable> T orElseThrow(Supplier<? extends X> e) throws X;
/** /**
* Converts the Maybe into either a single value stream or an empty stream. * Converts the Maybe into either a single value stream or an empty stream.
* *
* @return a Stream containing the value or nothing. * @return a Stream containing the value or nothing.
*/ */
public abstract Stream<T> stream(); Stream<T> stream();
/** /**
* Filter a Maybe by the predicate, replacing with Nothing when it fails. * Filter a Maybe by the predicate, replacing with Nothing when it fails.
@ -166,7 +166,7 @@ public interface Maybe<T> extends Functor<T, Maybe<?>> {
* @param predicate the test * @param predicate the test
* @return the Maybe, or Nothing if the test returns false * @return the Maybe, or Nothing if the test returns false
*/ */
public abstract Maybe<T> filter(Predicate<T> predicate); Maybe<T> filter(Predicate<T> predicate);
/** /**
* Provide the value within the Maybe, if it exists, to the Consumer, and returns this Maybe. * Provide the value within the Maybe, if it exists, to the Consumer, and returns this Maybe.
@ -174,14 +174,14 @@ public interface Maybe<T> extends Functor<T, Maybe<?>> {
* @param consumer the Consumer to the value if present * @param consumer the Consumer to the value if present
* @return this Maybe * @return this Maybe
*/ */
public abstract Maybe<T> peek(Consumer<T> consumer); Maybe<T> peek(Consumer<T> consumer);
/** /**
* Run the runnable if the Maybe is a Nothing, otherwise do nothing. * Run the runnable if the Maybe is a Nothing, otherwise do nothing.
* *
* @param runnable the runnable to call if this is a 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. * 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<T> extends Functor<T, Maybe<?>> {
* @param justMatcher the Consumer to pass the value of a Just to * @param justMatcher the Consumer to pass the value of a Just to
* @param nothingMatcher the Runnable to call if the Maybe is a Nothing * @param nothingMatcher the Runnable to call if the Maybe is a Nothing
*/ */
public abstract void match(Consumer<T> justMatcher, Runnable nothingMatcher); void match(Consumer<T> justMatcher, Runnable nothingMatcher);
/** /**
* Matches the Maybe, either just or nothing, and performs either the Function, for Just, or Supplier for nothing. * 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<T> extends Functor<T, Maybe<?>> {
* *
* @return the matched result, from either the justMatcher or the nothingMatcher * @return the matched result, from either the justMatcher or the nothingMatcher
*/ */
public abstract <R> R matchValue(Function<T, R> justMatcher, Supplier<R> nothingMatcher); <R> R matchValue(Function<T, R> justMatcher, Supplier<R> nothingMatcher);
/** /**
* Maps the Maybe into another Maybe only when it is nothing. * Maps the Maybe into another Maybe only when it is nothing.
@ -213,5 +213,5 @@ public interface Maybe<T> extends Functor<T, Maybe<?>> {
* *
* @return the original Maybe if not nothing, or the alternative * @return the original Maybe if not nothing, or the alternative
*/ */
public abstract Maybe<T> or(Supplier<Maybe<T>> alternative); Maybe<T> or(Supplier<Maybe<T>> alternative);
} }

View file

@ -50,7 +50,7 @@ final class Nothing<T> implements Maybe<T> {
} }
@Override @Override
@SuppressWarnings("unchecked") @SuppressWarnings({"unchecked", "PMD.AvoidDuplicateLiterals"})
public <R> Maybe<R> flatMap(final Function<T, Maybe<R>> f) { public <R> Maybe<R> flatMap(final Function<T, Maybe<R>> f) {
return (Maybe<R>) INSTANCE; return (Maybe<R>) INSTANCE;
} }

View file

@ -30,6 +30,8 @@ package net.kemitix.mon.result;
*/ */
public final class CheckedErrorResultException extends Exception { public final class CheckedErrorResultException extends Exception {
private static final long serialVersionUID = 6388860431020999870L;
private CheckedErrorResultException(final Throwable cause) { private CheckedErrorResultException(final Throwable cause) {
super(cause); super(cause);
} }

View file

@ -37,7 +37,7 @@ import java.util.function.Predicate;
* @param <T> the type of the value in the Result if it has been a success * @param <T> the type of the value in the Result if it has been a success
*/ */
@RequiredArgsConstructor @RequiredArgsConstructor
@SuppressWarnings("methodcount") @SuppressWarnings({"methodcount", "PMD.CyclomaticComplexity"})
class Err<T> implements Result<T> { class Err<T> implements Result<T> {
private final Throwable error; private final Throwable error;

View file

@ -30,6 +30,8 @@ package net.kemitix.mon.result;
*/ */
public final class ErrorResultException extends RuntimeException { public final class ErrorResultException extends RuntimeException {
private static final long serialVersionUID = -949688489831104844L;
private ErrorResultException(final Throwable cause) { private ErrorResultException(final Throwable cause) {
super(cause); super(cause);
} }

View file

@ -33,7 +33,7 @@ import java.util.function.*;
* @param <T> the type of the result when a success * @param <T> the type of the result when a success
* @author Paul Campbell (pcampbell@kemitix.net) * @author Paul Campbell (pcampbell@kemitix.net)
*/ */
@SuppressWarnings("methodcount") @SuppressWarnings({"methodcount", "PMD.TooManyMethods"})
public interface Result<T> extends Functor<T, Result<?>> { public interface Result<T> extends Functor<T, Result<?>> {
/** /**
@ -44,7 +44,7 @@ public interface Result<T> extends Functor<T, Result<?>> {
* @param <T> the type of the Maybe and the Result * @param <T> 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 * @return a Result containing the value of the Maybe when it is a Just, or the error when it is Nothing
*/ */
public static <T> Result<T> fromMaybe(final Maybe<T> maybe, final Supplier<Throwable> error) { static <T> Result<T> fromMaybe(final Maybe<T> maybe, final Supplier<Throwable> error) {
return maybe.map(Result::ok) return maybe.map(Result::ok)
.orElseGet(() -> Result.error(error.get())); .orElseGet(() -> Result.error(error.get()));
} }
@ -56,7 +56,7 @@ public interface Result<T> extends Functor<T, Result<?>> {
* @param <T> the type had the result been a success * @param <T> the type had the result been a success
* @return an error Result * @return an error Result
*/ */
public default <T> Result<T> err(final Throwable error) { default <T> Result<T> err(final Throwable error) {
return new Err<>(error); return new Err<>(error);
} }
@ -67,7 +67,7 @@ public interface Result<T> extends Functor<T, Result<?>> {
* @param <T> the type had the result been a success * @param <T> the type had the result been a success
* @return an error Result * @return an error Result
*/ */
public static <T> Result<T> error(final Throwable error) { static <T> Result<T> error(final Throwable error) {
return new Err<>(error); return new Err<>(error);
} }
@ -78,11 +78,11 @@ public interface Result<T> extends Functor<T, Result<?>> {
* @param <T> the type of the value * @param <T> the type of the value
* @return a Result * @return a Result
*/ */
@SuppressWarnings("illegalcatch") @SuppressWarnings({"illegalcatch", "PMD.AvoidCatchingThrowable", "PMD.AvoidDuplicateLiterals"})
public default <T> Result<T> result(final Callable<T> callable) { default <T> Result<T> result(final Callable<T> callable) {
try { try {
return Result.ok(callable.call()); return Result.ok(callable.call());
} catch (final Exception e) { } catch (final Throwable e) {
return Result.error(e); return Result.error(e);
} }
} }
@ -94,11 +94,11 @@ public interface Result<T> extends Functor<T, Result<?>> {
* @param <T> the type of the value * @param <T> the type of the value
* @return a Result * @return a Result
*/ */
@SuppressWarnings("illegalcatch") @SuppressWarnings({"illegalcatch", "PMD.AvoidCatchingThrowable"})
public static <T> Result<T> of(final Callable<T> callable) { static <T> Result<T> of(final Callable<T> callable) {
try { try {
return Result.ok(callable.call()); return Result.ok(callable.call());
} catch (final Exception e) { } catch (final Throwable e) {
return Result.error(e); return Result.error(e);
} }
} }
@ -110,7 +110,7 @@ public interface Result<T> extends Functor<T, Result<?>> {
* @param <T> the type of the value * @param <T> the type of the value
* @return a successful Result * @return a successful Result
*/ */
public default <T> Result<T> success(final T value) { default <T> Result<T> success(final T value) {
return new Success<>(value); return new Success<>(value);
} }
@ -121,7 +121,7 @@ public interface Result<T> extends Functor<T, Result<?>> {
* @param <T> the type of the value * @param <T> the type of the value
* @return a successful Result * @return a successful Result
*/ */
public static <T> Result<T> ok(final T value) { static <T> Result<T> ok(final T value) {
return new Success<>(value); return new Success<>(value);
} }
@ -134,7 +134,7 @@ public interface Result<T> extends Functor<T, Result<?>> {
* @param <T> the type of the Maybe and the Result * @param <T> 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 * @return a Result containing the value of the Maybe when it is a Just, or the error when it is Nothing
*/ */
public static <T> Maybe<T> toMaybe(final Result<T> result) { static <T> Maybe<T> toMaybe(final Result<T> result) {
try { try {
return Maybe.just(result.orElseThrow()); return Maybe.just(result.orElseThrow());
} catch (final CheckedErrorResultException throwable) { } catch (final CheckedErrorResultException throwable) {
@ -148,7 +148,7 @@ public interface Result<T> extends Functor<T, Result<?>> {
* @return the value if a success * @return the value if a success
* @throws CheckedErrorResultException if the result is an error * @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. * Extracts the successful value from the result, or throws the error Throwable.
@ -159,14 +159,14 @@ public interface Result<T> extends Functor<T, Result<?>> {
* @return the value if a success * @return the value if a success
* @throws E if the result is an error * @throws E if the result is an error
*/ */
public abstract <E extends Exception> T orElseThrow(Class<E> type) throws E; <E extends Exception> T orElseThrow(Class<E> type) throws E;
/** /**
* Extracts the successful value from the result, or throws the error in a {@link UnexpectedErrorResultException}. * Extracts the successful value from the result, or throws the error in a {@link UnexpectedErrorResultException}.
* *
* @return the value if a success * @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. * Swaps the inner Result of a Maybe, so that a Result is on the outside.
@ -177,7 +177,7 @@ public interface Result<T> extends Functor<T, Result<?>> {
* original Maybe. If the original Maybe is Nothing, the Result will contain Nothing. If the original Result was an * 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. * error, then the Result will also be an error.
*/ */
public static <T> Result<Maybe<T>> swap(final Maybe<Result<T>> maybeResult) { static <T> Result<Maybe<T>> swap(final Maybe<Result<T>> maybeResult) {
return maybeResult.orElseGet(() -> Result.ok(null)) return maybeResult.orElseGet(() -> Result.ok(null))
.flatMap(value -> Result.ok(Maybe.maybe(value))); .flatMap(value -> Result.ok(Maybe.maybe(value)));
} }
@ -189,7 +189,7 @@ public interface Result<T> extends Functor<T, Result<?>> {
* @param <R> the type of the value withing the Result of the mapping function * @param <R> the type of the value withing the Result of the mapping function
* @return a Result * @return a Result
*/ */
public abstract <R> Result<R> flatMap(Function<T, Result<R>> f); <R> Result<R> flatMap(Function<T, Result<R>> f);
/** /**
* Applies the function to the contents of a Maybe within the Result. * Applies the function to the contents of a Maybe within the Result.
@ -200,7 +200,7 @@ public interface Result<T> extends Functor<T, Result<?>> {
* @param <R> the type of the updated Result * @param <R> the type of the updated Result
* @return a new Maybe within a Result * @return a new Maybe within a Result
*/ */
public static <T, R> Result<Maybe<R>> flatMapMaybe( static <T, R> Result<Maybe<R>> flatMapMaybe(
final Result<Maybe<T>> maybeResult, final Result<Maybe<T>> maybeResult,
final Function<Maybe<T>, Result<Maybe<R>>> f final Function<Maybe<T>, Result<Maybe<R>>> f
) { ) {
@ -212,17 +212,17 @@ public interface Result<T> extends Functor<T, Result<?>> {
* *
* @return true if the Result is an error. * @return true if the Result is an error.
*/ */
public abstract boolean isError(); boolean isError();
/** /**
* Checks if the Result is a success. * Checks if the Result is a success.
* *
* @return true if the Result is a success. * @return true if the Result is a success.
*/ */
public abstract boolean isOkay(); boolean isOkay();
@Override @Override
public abstract <R> Result<R> map(Function<T, R> f); <R> Result<R> map(Function<T, R> f);
/** /**
* Matches the Result, either success or error, and supplies the appropriate Consumer with the value or error. * Matches the Result, either success or error, and supplies the appropriate Consumer with the value or error.
@ -230,7 +230,7 @@ public interface Result<T> extends Functor<T, Result<?>> {
* @param onSuccess the Consumer to pass the value of a successful Result to * @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 * @param onError the Consumer to pass the error from an error Result to
*/ */
public abstract void match(Consumer<T> onSuccess, Consumer<Throwable> onError); void match(Consumer<T> onSuccess, Consumer<Throwable> onError);
/** /**
* Wraps the value within the Result in a Maybe, either a Just if the predicate is true, or Nothing. * 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<T> extends Functor<T, Result<?>> {
* @param predicate the test to decide * @param predicate the test to decide
* @return a Result containing a Maybe that may or may not contain a value * @return a Result containing a Maybe that may or may not contain a value
*/ */
public abstract Result<Maybe<T>> maybe(Predicate<T> predicate); Result<Maybe<T>> maybe(Predicate<T> predicate);
/** /**
* Provide the value within the Result, if it is a success, to the Consumer, and returns this Result. * 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<T> extends Functor<T, Result<?>> {
* @param consumer the Consumer to the value if a success * @param consumer the Consumer to the value if a success
* @return this Result * @return this Result
*/ */
public abstract Result<T> peek(Consumer<T> consumer); Result<T> peek(Consumer<T> consumer);
/** /**
* Provide a way to attempt to recover from an error state. * Provide a way to attempt to recover from an error state.
@ -254,7 +254,7 @@ public interface Result<T> extends Functor<T, Result<?>> {
* @param f the function to recover from the error * @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. * @return a new Result, either a Success, or if recovery is not possible an other Err.
*/ */
public abstract Result<T> recover(Function<Throwable, Result<T>> f); Result<T> recover(Function<Throwable, Result<T>> f);
/** /**
* A handler for error states. * A handler for error states.
@ -264,7 +264,7 @@ public interface Result<T> extends Functor<T, Result<?>> {
* *
* @param errorConsumer the consumer to handle the error * @param errorConsumer the consumer to handle the error
*/ */
public abstract void onError(Consumer<Throwable> errorConsumer); void onError(Consumer<Throwable> errorConsumer);
/** /**
* Maps a Success Result to another Result using a Callable that is able to throw a checked exception. * 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<T> extends Functor<T, Result<?>> {
* @param <R> the type of the final Result * @param <R> the type of the final Result
* @return a new Result * @return a new Result
*/ */
public abstract <R> Result<R> andThen(Function<T, Callable<R>> f); <R> Result<R> andThen(Function<T, Callable<R>> f);
/** /**
* Perform the continuation with the current Result value then return the current Result, assuming there was no * Perform the continuation with the current Result value then return the current Result, assuming there was no
@ -304,7 +304,7 @@ public interface Result<T> extends Functor<T, Result<?>> {
* @param f the function to map the Success value into the result continuation * @param f the function to map the Success value into the result continuation
* @return the Result or a new error Result * @return the Result or a new error Result
*/ */
public abstract Result<T> thenWith(Function<T, WithResultContinuation<T>> f); Result<T> thenWith(Function<T, WithResultContinuation<T>> f);
/** /**
* Reduce two Results of the same type into one using the reducing function provided. * Reduce two Results of the same type into one using the reducing function provided.
@ -316,5 +316,5 @@ public interface Result<T> extends Functor<T, Result<?>> {
* @param operator the function to combine the values the Results * @param operator the function to combine the values the Results
* @return a Result containing the combination of the two Results * @return a Result containing the combination of the two Results
*/ */
public abstract Result<T> reduce(Result<T> identify, BinaryOperator<T> operator); Result<T> reduce(Result<T> identify, BinaryOperator<T> operator);
} }

View file

@ -37,7 +37,7 @@ import java.util.function.Predicate;
* @param <T> the type of the value in the Result * @param <T> the type of the value in the Result
*/ */
@RequiredArgsConstructor @RequiredArgsConstructor
@SuppressWarnings("methodcount") @SuppressWarnings({"methodcount", "PMD.CyclomaticComplexity"})
class Success<T> implements Result<T> { class Success<T> implements Result<T> {
private final T value; private final T value;
@ -53,8 +53,13 @@ class Success<T> implements Result<T> {
} }
@Override @Override
@SuppressWarnings({"illegalcatch", "PMD.AvoidCatchingThrowable"})
public <R> Result<R> map(final Function<T, R> f) { public <R> Result<R> map(final Function<T, R> f) {
return success(f.apply(value)); try {
return success(f.apply(this.value));
} catch (Throwable e) {
return err(e);
}
} }
@Override @Override

View file

@ -31,6 +31,8 @@ package net.kemitix.mon.result;
*/ */
public final class UnexpectedErrorResultException extends RuntimeException { public final class UnexpectedErrorResultException extends RuntimeException {
private static final long serialVersionUID = 959355287011172256L;
private UnexpectedErrorResultException(final Throwable cause) { private UnexpectedErrorResultException(final Throwable cause) {
super(cause); super(cause);
} }

View file

@ -35,10 +35,18 @@ public interface WithResultContinuation<T> {
* *
* @throws Exception to replace the current Result with an error * @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<T> call(final Result<T> 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<T> call(final Result<T> currentResult) {
try { try {
run(); run();
} catch (Throwable e) { } catch (Throwable e) {

View file

@ -41,8 +41,8 @@ import java.util.function.Function;
@EqualsAndHashCode @EqualsAndHashCode
class GeneralisedTree<T> implements Tree<T>, TreeMapper<T> { class GeneralisedTree<T> implements Tree<T>, TreeMapper<T> {
private final T item; private transient final T item;
private final List<Tree<T>> subTrees; private transient final List<Tree<T>> subTrees;
/** /**
* Creates a new tree. * Creates a new tree.

View file

@ -43,9 +43,9 @@ import java.util.stream.Collectors;
@SuppressWarnings("methodcount") @SuppressWarnings("methodcount")
class MutableTree<T> implements Tree<T>, TreeMapper<T> { class MutableTree<T> implements Tree<T>, TreeMapper<T> {
private final List<MutableTree<T>> mySubTrees = new ArrayList<>(); private transient final List<MutableTree<T>> mySubTrees = new ArrayList<>();
private T item; private transient T item;
/** /**
* Create a new {@link MutableTree}. * Create a new {@link MutableTree}.

View file

@ -35,7 +35,7 @@ import java.util.function.Function;
*/ */
class MutableTreeBuilder<T> implements TreeBuilder<T> { class MutableTreeBuilder<T> implements TreeBuilder<T> {
private final MutableTree<T> root; private transient final MutableTree<T> root;
/** /**
* Create empty tree builder. * Create empty tree builder.

View file

@ -47,7 +47,7 @@ public interface Tree<T> extends Functor<T, Tree<?>> {
* *
* @return a empty generalised tree * @return a empty generalised tree
*/ */
public static <R> Tree<R> leaf(final R item) { static <R> Tree<R> leaf(final R item) {
return new GeneralisedTree<>(item, Collections.emptyList()); return new GeneralisedTree<>(item, Collections.emptyList());
} }
@ -59,7 +59,7 @@ public interface Tree<T> extends Functor<T, Tree<?>> {
* @param <R> the type of the item * @param <R> the type of the item
* @return a leaf node of a generalised tree * @return a leaf node of a generalised tree
*/ */
public static <R> Tree<R> of(final R item, final Collection<Tree<R>> subtrees) { static <R> Tree<R> of(final R item, final Collection<Tree<R>> subtrees) {
return new GeneralisedTree<>(item, subtrees); return new GeneralisedTree<>(item, subtrees);
} }
@ -73,7 +73,7 @@ public interface Tree<T> extends Functor<T, Tree<?>> {
*/ */
@SuppressFBWarnings(value = "UP_UNUSED_PARAMETER", @SuppressFBWarnings(value = "UP_UNUSED_PARAMETER",
justification = "Use the type parameter to fingerprint the return type") justification = "Use the type parameter to fingerprint the return type")
public static <B> TreeBuilder<B> builder(final Class<B> type) { static <B> TreeBuilder<B> builder(final Class<B> type) {
return new MutableTreeBuilder<>(); return new MutableTreeBuilder<>();
} }
@ -85,26 +85,26 @@ public interface Tree<T> extends Functor<T, Tree<?>> {
* *
* @return a TreeBuilder * @return a TreeBuilder
*/ */
public static <B> TreeBuilder<B> builder(final Tree<B> tree) { static <B> TreeBuilder<B> builder(final Tree<B> tree) {
return new MutableTreeBuilder<>(MutableTree.of(tree)); return new MutableTreeBuilder<>(MutableTree.of(tree));
} }
@Override @Override
public abstract <R> Tree<R> map(Function<T, R> f); <R> Tree<R> map(Function<T, R> f);
/** /**
* Return the item within the node of the tree, if present. * Return the item within the node of the tree, if present.
* *
* @return a Maybe containing the item * @return a Maybe containing the item
*/ */
public abstract Maybe<T> item(); Maybe<T> item();
/** /**
* Count the number of item in the tree, including subtrees. * 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 * @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) return item().matchValue(x -> 1, () -> 0)
+ subTrees().stream().mapToInt(Tree::count).sum(); + subTrees().stream().mapToInt(Tree::count).sum();
} }
@ -114,5 +114,5 @@ public interface Tree<T> extends Functor<T, Tree<?>> {
* *
* @return a list of Trees * @return a list of Trees
*/ */
public abstract List<Tree<T>> subTrees(); List<Tree<T>> subTrees();
} }

View file

@ -39,7 +39,7 @@ public interface TreeBuilder<T> {
* *
* @return a {@link Tree} * @return a {@link Tree}
*/ */
public abstract Tree<T> build(); Tree<T> build();
/** /**
* Set the current {@link Tree}'s item. * Set the current {@link Tree}'s item.
@ -48,7 +48,7 @@ public interface TreeBuilder<T> {
* *
* @return the TreeBuilder * @return the TreeBuilder
*/ */
public abstract TreeBuilder<T> item(T item); TreeBuilder<T> item(T item);
/** /**
* Adds the subtree to the current tree. * Adds the subtree to the current tree.
@ -57,7 +57,7 @@ public interface TreeBuilder<T> {
* *
* @return the TreeBuilder * @return the TreeBuilder
*/ */
public abstract TreeBuilder<T> add(Tree<T> subtree); TreeBuilder<T> add(Tree<T> subtree);
/** /**
* Add the Child item as a subTree. * Add the Child item as a subTree.
@ -65,7 +65,7 @@ public interface TreeBuilder<T> {
* @param childItem the item to add as a subtree * @param childItem the item to add as a subtree
* @return the TreeBuilder * @return the TreeBuilder
*/ */
public abstract TreeBuilder<T> addChild(T childItem); TreeBuilder<T> addChild(T childItem);
/** /**
* Add all the child items as subTrees. * Add all the child items as subTrees.
@ -73,7 +73,7 @@ public interface TreeBuilder<T> {
* @param children the items to add as a subtree * @param children the items to add as a subtree
* @return the TreeBuilder * @return the TreeBuilder
*/ */
public default TreeBuilder<T> addChildren(List<T> children) { default TreeBuilder<T> addChildren(List<T> children) {
children.forEach(this::addChild); children.forEach(this::addChild);
return this; return this;
} }
@ -84,5 +84,5 @@ public interface TreeBuilder<T> {
* @param childItem the item of search the subtrees for * @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 * @return a Maybe containing the TreeBuilder for the subtree, or Nothing if there child item is not found
*/ */
public abstract Maybe<TreeBuilder<T>> select(T childItem); Maybe<TreeBuilder<T>> select(T childItem);
} }

View file

@ -43,7 +43,7 @@ interface TreeMapper<T> {
* *
* @return a List of mapped sub-trees * @return a List of mapped sub-trees
*/ */
public default <R> List<Tree<R>> mapTrees( default <R> List<Tree<R>> mapTrees(
final Function<T, R> f, final Function<T, R> f,
final List<Tree<T>> trees final List<Tree<T>> trees
) { ) {

View file

@ -144,7 +144,7 @@ class ResultTest implements WithAssertions {
} }
@Test @Test
void okay_whenMap_isOkay() { void okay_whenMapToOkay_isOkay() {
//given //given
final Result<Integer> okResult = Result.ok(1); final Result<Integer> okResult = Result.ok(1);
//when //when
@ -157,6 +157,22 @@ class ResultTest implements WithAssertions {
); );
} }
@Test
void okay_whenMapToError_isError() {
//given
final Result<Integer> okResult = Result.ok(1);
//when
final Result<String> 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 @Test
void error_whenMap_isError() { void error_whenMap_isError() {
//given //given