Paul Campbell
571fd2641a
* pom: don’t override java version - use LTS - JDK 11 * result: nest tests * Add Result.{from(Either|Optional),ofVoid,ok(),onSuccess,applyOver} * Add Result.flatApplyOver * result: create separate documentation file * Add Result.toEither() * Add Result.onError - by type * Simplify creation of Result in Result.ofVoid * Make Result.ofVoid tolerate exceptions and no return statement * Add missing javadoc for result.VoidCallable * Bump pitest-junit5-plugin from 0.12 to 0.14 (#187) * Bump tiles-maven-plugin from 2.19 to 2.20 (#188) * Bump lombok from 1.18.18 to 1.18.20 (#189) * Bump spotbugs-annotations from 4.2.2 to 4.2.3 (#191) * Bump mockito-junit-jupiter from 3.8.0 to 3.9.0 (#190) * Bump pitest-maven from 1.6.4 to 1.6.5 (#192) * Bump pitest-maven from 1.6.5 to 1.6.6 (#194) * Bump tiles-maven-plugin from 2.20 to 2.21 (#195) * Bump mockito-junit-jupiter from 3.9.0 to 3.10.0 (#196) * Bump junit-bom from 5.7.1 to 5.7.2 (#197) * Bump tiles-maven-plugin from 2.21 to 2.22 (#198) * Bump pitest-maven from 1.6.6 to 1.6.7 (#199) * Bump mockito-junit-jupiter from 3.10.0 to 3.11.0 (#200) * Bump assertj-core from 3.19.0 to 3.20.0 (#202) * Bump mockito-junit-jupiter from 3.11.0 to 3.11.1 (#201) * Bump assertj-core from 3.20.0 to 3.20.1 (#203) * Bump tiles-maven-plugin from 2.22 to 2.23 (#204) * Bump assertj-core from 3.20.1 to 3.20.2 (#205) * Bump mockito-junit-jupiter from 3.11.1 to 3.11.2 (#206) * Upgrade to GitHub-native Dependabot (#193) Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> Co-authored-by: Paul Campbell <pcampbell@kemitix.net> * Create dependabot.yml (#207) * Bump actions/setup-java from 1 to 2.1.0 (#210) * Bump actions/setup-java from 1 to 2.1.0 Bumps [actions/setup-java](https://github.com/actions/setup-java) from 1 to 2.1.0. - [Release notes](https://github.com/actions/setup-java/releases) - [Commits](https://github.com/actions/setup-java/compare/v1...v2.1.0) --- updated-dependencies: - dependency-name: actions/setup-java dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> * Update build-maven.yml * Update deploy-sonatype.yml * Update build-maven.yml * Update build-maven.yml Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Paul Campbell <pcampbell@kemitix.net> * Bump release-drafter/release-drafter from 5.14.0 to 5.15.0 (#209) Bumps [release-drafter/release-drafter](https://github.com/release-drafter/release-drafter) from 5.14.0 to 5.15.0. - [Release notes](https://github.com/release-drafter/release-drafter/releases) - [Commits](https://github.com/release-drafter/release-drafter/compare/v5.14.0...v5.15.0) --- updated-dependencies: - dependency-name: release-drafter/release-drafter dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Paul Campbell <pcampbell@kemitix.net> * Add ResultVoid special Result type - Result now extends ThrowableFunctor allowing mapping functions to throw exceptions - Added ResultVoid to replace the usage of Result<Void> and handle void cases better - Add `flatMapV` and `andThenV` which return ResultVoid * result: Migrate static constructor docs to javadoc * result.Result: move static constructors to top of class * result: add docs and tests for from(Maybe) and from(Either) * pom: add apigaurdian dependency * Remove migrated docs * result: add missing api status * result.Result: group static methods together in file * result.Result: reorder static methods * result.Result: deprecate static maybe methods that don’t include Maybe in name * result.Result.toMaybe: update docs and tests * fix up toMaybe example * result.Result.flatMapMaybe: update docs and tests * result.Result.applyOver(Stream,Consumer): updated docs and tests * result.Result.applyOver: update docs and tests * result.Result: fix bad javadoc encoding * result.Result.flatMapApply: update docs and tests * result.Result.error(Class, Throwable): added * result.Result: suppress warning on long class * result.Result: apply warning supression to entire interface * result.Result.toEither: update docs and tests * result.Result.err: deprecate * [BREAKING] result.Result.err: removed * [BREAKING] result.Result.result: remove method * [BREAKING] result.Result.success: remove method * result.Result.orElseThrow: update docs and tests * result.Result.toVoid: add javadoc and test * result.Result.map: update docs and tests * result.Result.map: add api status * result.Result.isError: update docs and test * result.Result.isError: update docs and test * result.Result.isOkay: update docs and test * result.Result.onError: update docs and test * result.Result.onError: update docs and tests * result.Result: tidy up and add some API statuses * result.Result.orElseThrowUnchecked: update docs and tests * result.Result.orElseThrow: update docs and tests * result.Result.maybe: removed * result.Result.thenWith: update docs and test * result.Result.peek: update docs and tests * result.Result.recover: update docs and tests * result.Result.match: update docs and tests * result.Result.andThen: deprecate in favour of map * result.Result.flatMap: update docs and tests * Update link to documentation for Result * result.Result.flatMapV: add docs and test * result.Result.match: update docs * result.Result.onSuccess: update docs * result.Result.reduce: mark experimental * pom: remove java.version (conflict resolution mistake) * Result: add deprecation annotations * result.Err: remove unused imports * result.BaseResult: extract interface * result.ResultVoid.match: add javadoc * result.ResultVoid: regroup tests and add javadoc for recover and onSuccess * result.ResultVoid.onError: update docs and tests * result.Err.andThen: don't create new object needlessly * result.ResultVoid.andThen: add javadoc and update tests * result.ResultVoid.inject: on error returns new exception, update docs and tests * result.Result.error: suppress false positive unused parameter warning * result.Success.getInstance added * ErrVoid: fix toString * SuccessVoid: break circular dependency * Err: break circular dependency * Result.result: added * Success: break cyclic dependency * Result.result: add javadoc and test * Result.result: reuse static of(…) * github/dependabot: clean up * github/workflow: remove unused graphviz * github/workflow: drop java support Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
803 lines
20 KiB
Markdown
803 lines
20 KiB
Markdown
# Mon
|
|
|
|
Wrapper, TypeAlias, Maybe, Result, Tree, Lazy, Either 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) - light-weight type-alias-like
|
|
- [TypeAlias](#TypeAlias) - type-alias-like monadic wrapper
|
|
- [Maybe](#Maybe) - Maybe, Just or Nothing
|
|
- [Result](https://kemitix.github.io/mon/net/kemitix/mon/result/package-summary.html) - Result, Success or Err
|
|
- [Tree](#Tree) - generic trees
|
|
- [Lazy](#Lazy) - lazy evaluation
|
|
- [Either](#Either) - Either, Left or Right
|
|
- [Combinators](#Combinators) - Before, After or Around
|
|
|
|
---
|
|
## 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`.
|
|
|
|
### Skip the Mon import
|
|
|
|
If the only thing you want is `Wrapper`, you can skip importing the `mon`
|
|
dependency by declaring your types like so:
|
|
|
|
``` java
|
|
interface PhoneNumber {String value();}
|
|
```
|
|
|
|
This is functionally identical to the example above using `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();
|
|
```
|
|
---
|
|
|
|
## 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
|
|
|
|
Taken from [The Bounds of Java Newsletter #3](https://github.com/boundsofjava/boj-newsletter-003/tree/master/src/main/java/com/boundsofjava/newsletter/introducingcombinators), although the associated article isn't online anymore.
|
|
|
|
### After
|
|
|
|
Attach a `BiConsumer` to a `Function`, so that when the `Function` is called,
|
|
the `BiConsumer` is called afterwards, receiving the original argument to the
|
|
`Function` plus the result.
|
|
|
|
#### Example
|
|
|
|
``` java
|
|
BiConsumer<BigDecimal, String> after =
|
|
(amount, result) ->
|
|
System.out.println("Amount was " + amount + ", Result is " + result);
|
|
|
|
var tax = BigDecimal.valueOf("1.22");
|
|
Function<BigDecimal, String> addTax =
|
|
amount -> "$" + amount.multiply(tax);
|
|
|
|
Function<BigDecimal, String> addTaxDecorated =
|
|
After.decorate(addTax, after);
|
|
|
|
var amount = BigDecimal.valueOf("1000");
|
|
String result = addTaxDecorated.apply(amount);
|
|
```
|
|
---
|
|
#### `static <T, R> Function<T, R> After.decorate(Function<T, R> function, BiConsumer<T, R> after)`
|
|
|
|
Creates a new decorated `Function`.
|
|
|
|
---
|
|
### Before
|
|
|
|
Attach a `Consumer` to a `Function`, so that when the `Function` is called,
|
|
the `Consumer` is called first, receiving the argument to the `Function`.
|
|
|
|
#### Example
|
|
|
|
``` java
|
|
Consumer<BigDecimal> before =
|
|
amount -> System.out.println("Amount is " + amount);
|
|
|
|
var tax = BigDecimal.valueOf("1.22");
|
|
Function<BigDecimal, String> addTax =
|
|
amount -> "$" + amount.multiply(tax);
|
|
|
|
Function<BigDecimal, String> addTaxDecorated =
|
|
Before.decorate(before, addTax);
|
|
|
|
var amount = BigDecimal.valueOf("1000");
|
|
String result = addTaxDecorated.apply(amount);
|
|
```
|
|
|
|
#### `static <T, R> Function<T, R> decorate(Consumer<T> before, Function<T, R> function)`
|
|
|
|
Creates a new decorated `Function`.
|
|
|
|
### Around
|
|
|
|
Attach a `BiConsumer` to a `Function`, so that when the `Function` is called,
|
|
the `BiConsumer` is called with an `Around.Executable` that will invoke the `Function`.
|
|
The `BiConsumer` is responsible for calling `execute()` on the `Around.Executable` in
|
|
order to invoke the `Function`.
|
|
The `BiConsumer` can perform actions before and after calling `execute()` on the
|
|
`Around.Executable`.
|
|
|
|
#### Example
|
|
|
|
``` java
|
|
BiConsumer<Around.Executable<String>, BigDecimal> around =
|
|
(function, amount) -> {
|
|
System.out.println("Amount is " + amount);
|
|
var result = function.execute(); // INVOKE THE FUNCTION
|
|
System.out.println("Result is " + result");
|
|
};
|
|
|
|
var tax = BigDecimal.valueOf("1.22");
|
|
Function<BigDecimal, String> addTax =
|
|
amount -> "$" + amount.multiply(tax);
|
|
|
|
Function<BigDecimal, String> addTaxDecorated =
|
|
Around.decorate(addTax, around);
|
|
|
|
var amount = BigDecimal.valueOf("1000");
|
|
String result = addTaxDecorated.apply(amount);
|
|
```
|
|
|
|
#### `static <T, R> Function<T, R> decorate(final Function<T, R> function, final BiConsumer<Executable<R>, T> around)`
|
|
|
|
Creates a new decorated `Function`.
|