mon/README.md
Paul Campbell 571fd2641a
Result enhancements (#215)
* 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>
2021-08-05 06:49:32 +01:00

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`.