diff --git a/CHANGELOG b/CHANGELOG
index 5db0e0f..990a1c9 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -7,6 +7,8 @@ CHANGELOG
* Add `Maybe.match(Consumer,Runnable)`
* Add `Maybe.isJust()`
* Add `Maybe.isNothing()`
+* BUG: `Maybe.orElseThrow()` now returns value when a Just
+* Rewrite README
0.10.0
------
diff --git a/README.md b/README.md
deleted file mode 100644
index 992cc5c..0000000
--- a/README.md
+++ /dev/null
@@ -1,203 +0,0 @@
-Mon
-===
-
-TypeAlias, Maybe and Result for Java
-
-[![Sonatype Nexus (Releases)](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/#search|ga|1|g%3A"net.kemitix"%20AND%20a%3A"mon")
-
-[![SonarQube Coverage](https://img.shields.io/sonar/https/sonarcloud.io/net.kemitix%3Amon/coverage.svg?style=for-the-badge)](https://sonarcloud.io/dashboard?id=net.kemitix%3Amon)
-[![SonarQube Tech Debt](https://img.shields.io/sonar/https/sonarcloud.io/net.kemitix%3Amon/tech_debt.svg?style=for-the-badge)](https://sonarcloud.io/dashboard?id=net.kemitix%3Amon)
-
-[![Codacy grade](https://img.shields.io/codacy/grade/d57096b0639d496aba9a7e43e7cf5b4c.svg?style=for-the-badge)](https://app.codacy.com/project/kemitix/mon/dashboard)
-[![jPeek](http://i.jpeek.org/net.kemitix/mon/badge.svg)](http://i.jpeek.org/net.kemitix/mon/index.html)
-
-## Maven
-
-```xml
-
- net.kemitix
- mon
- RELEASE
-
-```
-
-The latest version should be shown above with the nexus and maven-central badges.
-
-## Usage
-
-### TypeAlias
-
-More of a type-wrapper really. It's as close as I could get to a Haskell type alias in Java.
-
-```java
-class Goal extends TypeAlias {
- private Goal(final String goal) {
- super(goal);
- }
- public static Goal of(final String goal) {
- return new Goal(goal);
- }
-}
-```
-
-```java
-class Example {
- Goal goal = Goal.of("goal");
- void foo(final Goal goal) {
- System.out.println("The goal is " + goal.getValue());
- }
-}
-```
-
-### Maybe
-
-A Monad.
-
-A non-final substitute for Optional with `peek()` and `stream()` methods.
-
-```java
-class Test {
- @Test
- public void maybeTests() {
- // Constructors: maybe(T), just(T) and nothing()
- assertThat(Maybe.maybe(null)).isEqualTo(Maybe.nothing());
- assertThat(Maybe.maybe(1)).isEqualTo(Maybe.just(1));
- // .orElseGet(Supplier)
- assertThat(Maybe.nothing().orElseGet(() -> 1)).isEqualTo(1);
- assertThat(Maybe.just(1).orElseGet(() -> 2)).isEqualTo(1);
- // .orElse(Supplier)
- assertThat(Maybe.nothing().orElse(1)).isEqualTo(1);
- assertThat(Maybe.just(1).orElse(2)).isEqualTo(1);
- // .filter(Predicate)
- assertThat(Maybe.just(1).filter(v -> v > 2)).isEqualTo(Maybe.nothing());
- assertThat(Maybe.just(3).filter(v -> v > 2)).isEqualTo(Maybe.just(3));
- assertThat(Maybe.just(1).toOptional()).isEqualTo(Optional.of(1));
- assertThat(Maybe.nothing().toOptional()).isEqualTo(Optional.empty());
- // .fromOptional(Optional) is deprecated
- assertThat(Maybe.fromOptional(Optional.of(1))).isEqualTo(Maybe.just(1));
- assertThat(Maybe.fromOptional(Optional.empty())).isEqualTo(Maybe.nothing());
- // An alternative to using .fromOptional(Optional)
- assertThat(Optional.of(1).map(Maybe::just).orElse(Maybe::nothing)).isEqualTo(Maybe.just(1));
- assertThat(Optional.empty().map(Maybe::just).orElse(Maybe::nothing)).isEqualTo(Maybe.nothing());
- // .peek(Consumer)
- final AtomicInteger reference = new AtomicInteger(0);
- assertThat(Maybe.just(1).peek(reference::set)).isEqualTo(Maybe.just(1));
- assertThat(reference).hasValue(1);
- assertThat(Maybe.nothing().peek(v -> reference.incrementAndGet())).isEqualTo(Maybe.nothing());
- assertThat(reference).hasValue(1);
- // .orElseThrow(Supplier)
- assertThatCode(() -> Maybe.just(1).orElseThrow(IllegalStateException::new)).doesNotThrowAnyException();
- assertThatThrownBy(() -> Maybe.nothing().orElseThrow(IllegalStateException::new)).isInstanceOf(IllegalStateException.class);
- // .stream()
- assertThat(Maybe.just(1).stream()).containsExactly(1);
- assertThat(Maybe.nothing().stream()).isEmpty();
- }
-}
-```
-
-### Result
-
-A Monad.
-
-A container for method return values that may raise an Exception. Useful for when a checked exceptions can't be added
-to the method signature.
-
-```java
-package net.kemitix.mon;
-
-import net.kemitix.mon.result.Result;
-
-import java.io.IOException;
-
-class ResultExample implements Runnable {
-
- public static void main(String[] args) {
- new ResultExample().run();
- }
-
- @Override
- public void run() {
- System.out.println("run");
- final Result goodResult = goodMethod();
- if (goodResult.isOkay()) {
- doGoodThings();
- }
- if (goodResult.isError()) {
- notCalled(0);
- }
-
- goodResult.flatMap(number -> convertToString(number))
- .flatMap(str -> stringLength(str))
- .match(
- success -> System.out.format("Length is %s%n", success),
- error -> System.out.println("Count not determine length")
- );
-
- final Result badResult = badMethod();
- badResult.match(
- success -> notCalled(success),
- error -> handleError(error)
- );
- }
-
- private Result goodMethod() {
- System.out.println("goodMethod");
- return Result.ok(1);
- }
-
- private void doGoodThings() {
- System.out.println("doGoodThings");
- }
-
- private void notCalled(final Integer success) {
- System.out.println("notCalled");
- }
-
- private Result convertToString(final Integer number) {
- System.out.println("convertToString");
- return Result.ok(String.valueOf(number));
- }
-
- private Result stringLength(final String value) {
- System.out.println("stringLength");
- if (value == null) {
- return Result.error(new NullPointerException("value is null"));
- }
- return Result.ok(value.length());
- }
-
- // doesn't need to declare "throws IOException"
- private Result badMethod() {
- System.out.println("badMethod");
- return Result.error(new IOException("error"));
- }
-
- private void handleError(final Throwable error) {
- System.out.println("handleError");
- throw new RuntimeException("Handled exception", error);
- }
-
-}
-```
-Will output:
-```text
-run
-goodMethod
-doGoodThings
-convertToString
-stringLength
-Length is 1
-badMethod
-handleError
-Exception in thread "main" java.lang.RuntimeException: Handled exception
- at net.kemitix.mon.ResultExample.handleError(ResultExample.java:72)
- at net.kemitix.mon.ResultExample.lambda$run$5(ResultExample.java:34)
- at net.kemitix.mon.result.Err.match(Err.java:56)
- at net.kemitix.mon.ResultExample.run(ResultExample.java:32)
- at net.kemitix.mon.ResultExample.main(ResultExample.java:10)
-Caused by: java.io.IOException: error
- at net.kemitix.mon.ResultExample.badMethod(ResultExample.java:67)
- at net.kemitix.mon.ResultExample.run(ResultExample.java:31)
- ... 1 more
-```
diff --git a/README.org b/README.org
new file mode 100644
index 0000000..411f3e0
--- /dev/null
+++ b/README.org
@@ -0,0 +1,703 @@
+* Mon
+ :PROPERTIES:
+ :CUSTOM_ID: mon
+ :END:
+
+** TypeAlias, Maybe and Result for Java.
+
+ [[https://oss.sonatype.org/content/repositories/releases/net/kemitix/mon][file:https://img.shields.io/nexus/r/https/oss.sonatype.org/net.kemitix/mon.svg?style=for-the-badge]]
+ [[https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22net.kemitix%22%20AND%20a%3A%22mon%22][file:https://img.shields.io/maven-central/v/net.kemitix/mon.svg?style=for-the-badge]]
+
+ [[https://sonarcloud.io/dashboard?id=net.kemitix%3Amon][file:https://img.shields.io/sonar/https/sonarcloud.io/net.kemitix%3Amon/coverage.svg?style=for-the-badge#.svg]]
+ [[https://sonarcloud.io/dashboard?id=net.kemitix%3Amon][file:https://img.shields.io/sonar/https/sonarcloud.io/net.kemitix%3Amon/tech_debt.svg?style=for-the-badge#.svg]]
+ [[https://sonarcloud.io/dashboard?id=net.kemitix%3Amon][file:https://sonarcloud.io/api/project_badges/measure?project=net.kemitix%3Amon&metric=sqale_rating#.svg]]
+ [[https://sonarcloud.io/dashboard?id=net.kemitix%3Amon][file:https://sonarcloud.io/api/project_badges/measure?project=net.kemitix%3Amon&metric=alert_status#.svg]]
+ [[https://sonarcloud.io/dashboard?id=net.kemitix%3Amon][file:https://sonarcloud.io/api/project_badges/measure?project=net.kemitix%3Amon&metric=reliability_rating#.svg]]
+ [[https://sonarcloud.io/dashboard?id=net.kemitix%3Amon][file:https://sonarcloud.io/api/project_badges/measure?project=net.kemitix%3Amon&metric=security_rating#.svg]]
+ [[https://sonarcloud.io/dashboard?id=net.kemitix%3Amon][file:https://sonarcloud.io/api/project_badges/measure?project=net.kemitix%3Amon&metric=sqale_index#.svg]]
+ [[https://sonarcloud.io/dashboard?id=net.kemitix%3Amon][file:https://sonarcloud.io/api/project_badges/measure?project=net.kemitix%3Amon&metric=vulnerabilities#.svg]]
+ [[https://sonarcloud.io/dashboard?id=net.kemitix%3Amon][file:https://sonarcloud.io/api/project_badges/measure?project=net.kemitix%3Amon&metric=bugs#.svg]]
+ [[https://sonarcloud.io/dashboard?id=net.kemitix%3Amon][file:https://sonarcloud.io/api/project_badges/measure?project=net.kemitix%3Amon&metric=code_smells#.svg]]
+ [[https://sonarcloud.io/dashboard?id=net.kemitix%3Amon][file:https://sonarcloud.io/api/project_badges/measure?project=net.kemitix%3Amon&metric=ncloc#.svg]]
+
+ [[https://app.codacy.com/project/kemitix/mon/dashboard][file:https://img.shields.io/codacy/grade/d57096b0639d496aba9a7e43e7cf5b4c.svg?style=for-the-badge]]
+ [[http://i.jpeek.org/net.kemitix/mon/index.html][file:http://i.jpeek.org/net.kemitix/mon/badge.svg]]
+
+** Maven
+ :PROPERTIES:
+ :CUSTOM_ID: maven
+ :END:
+
+#+BEGIN_SRC xml
+
+ net.kemitix
+ mon
+ RELEASE
+
+#+END_SRC
+
+ The latest version should be shown above with the nexus and maven-central
+ badges or can be found on [[https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22net.kemitix%22%20AND%20a%3A%22mon%22][Maven Central]].
+
+** Usage
+ :PROPERTIES:
+ :CUSTOM_ID: usage
+ :END:
+
+*** TypeAlias
+ :PROPERTIES:
+ :CUSTOM_ID: typealias
+ :END:
+
+ In Haskell it is possible to create an alias for a Type, and to then use
+ that alias with the same behaviour as the original, except that the compiler
+ doesn't treat the alias as the same Type and will generate compiler errors
+ if you try and use them together. e.g.:
+
+ #+BEGIN_SRC haskell
+ type PhoneNumber = String
+ type Name = String
+ type PhoneBook = [(Name,PhoneNumber)]
+ #+END_SRC
+
+ In Java we don't have the ability to have that true alias, so TypeAlias is
+ more of a type-wrapper. It's as close as I could get to a Haskell type alias
+ in Java.
+
+ The benefits of using TypeAlias are:
+
+ - encapsulation of the wrapped type when passing references through code
+ that doesn't need to access the actual value, but only to pass it on
+ - type-safe parameters where you would otherwise be passing Strings,
+ Integers, Lists, or other general classes
+ - equality and hashcode
+ - less verbose than implementing your own
+
+ *TypeAlias Example:*
+
+ #+BEGIN_SRC java
+ class PhoneNumber extends TypeAlias {
+ private PhoneNumber(final String value) {
+ super(value);
+ }
+ public static PhoneNumber of(final String phoneNumber) {
+ return new PhoneNumber(phoneNumber);
+ }
+ }
+ #+END_SRC
+
+ *Roll your own:*
+
+ #+BEGIN_SRC java
+ class PhoneNumber {
+ private final String value;
+ private PhoneNumber(final String value) {
+ this.value = value;
+ }
+ public static PhoneNumber of(final String phoneNumber) {
+ return new PhoneNumber(phoneNumber);
+ }
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ PhoneNumber that = (PhoneNumber) o;
+ return Objects.equals(value, that.value);
+ }
+ @Override
+ public int hashCode() {
+ return Objects.hash(value);
+ }
+ public String getValue() {
+ return value;
+ }
+ }
+ #+END_SRC
+
+ *Lombok:*
+
+ Although, if you are using Lombok, that can be equally terse, both it and
+ TypeAlias coming in at 8 lines each, compared to 24 for rolling your
+ own:
+
+ #+BEGIN_SRC java
+ @Value
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class PhoneNumber {
+ private final String value;
+ public static PhoneNumber of(final String phoneNumber) {
+ return new PhoneNumber(phoneNumber);
+ }
+ }
+ #+END_SRC
+
+*** Maybe
+ :PROPERTIES:
+ :CUSTOM_ID: maybe
+ :END:
+
+ Allows specifying that a value may or may not be present. Similar to
+ =Optional=. =Maybe= provides additional methods that =Optional= doesn't:
+ =isNothing()=, =stream()=, =ifNothing()= and =match()=. =Maybe= does not
+ have a =get()= method.
+
+ Unlike =Optional=, when a =map()= results in a =null=, the =Maybe= will
+ continue to be a =Just=. =Optional= would switch to being empty. [[http://blog.vavr.io/the-agonizing-death-of-an-astronaut/][vavi.io
+ follows the same behaviour as =Maybe=]].
+
+ #+BEGIN_SRC java
+ import net.kemitix.mon.maybe.Maybe;
+
+ import java.util.function.Function;
+ import java.util.function.Predicate;
+
+ class MaybeExample {
+
+ public static void main(String[] args) {
+ Maybe.just(countArgs(args))
+ .filter(isEven())
+ .map(validMessage())
+ .match(
+ just -> System.out.println(just),
+ () -> System.out.println("Not an valid value")
+ );
+ }
+
+ private static Function validMessage() {
+ return v -> String.format("Value %d is even", v);
+ }
+
+ private static Predicate isEven() {
+ return v -> v % 2 == 0;
+ }
+
+ private static Integer countArgs(String[] args) {
+ return args.length;
+ }
+ }
+ #+END_SRC
+
+ In the above example, the number of command line arguments are counted, if
+ there are an even number of them then a message is created and printed by
+ the Consumer parameter in the =match= call. If there is an odd number of
+ arguments, then the filter will return =Maybe.nothing()=, meaning that the
+ =nothing= drops straight through the map and triggers the Runnable parameter
+ in the =match= call.
+
+**** =Maybe= is a Monad:
+
+ #+BEGIN_SRC java
+ package net.kemitix.mon;
+
+ import net.kemitix.mon.maybe.Maybe;
+ import org.assertj.core.api.WithAssertions;
+ import org.junit.Test;
+
+ import java.util.function.Function;
+
+ public class MaybeMonadTest implements WithAssertions {
+
+ private final int v = 1;
+ private final Function> f = i -> m(i * 2);
+ private final Function> g = i -> m(i + 6);
+
+ private static Maybe m(int value) {
+ return Maybe.maybe(value);
+ }
+
+ @Test
+ public void leftIdentity() {
+ assertThat(
+ m(v).flatMap(f)
+ ).isEqualTo(
+ f.apply(v)
+ );
+ }
+
+ @Test
+ public void rightIdentity() {
+ assertThat(
+ m(v).flatMap(x -> m(x))
+ ).isEqualTo(
+ m(v)
+ );
+ }
+
+ @Test
+ public void associativity() {
+ assertThat(
+ m(v).flatMap(f).flatMap(g)
+ ).isEqualTo(
+ m(v).flatMap(x -> f.apply(x).flatMap(g))
+ );
+ }
+
+ }
+ #+END_SRC
+
+**** Static Constructors
+
+***** =static Maybe maybe(T value)=
+
+ Create a Maybe for the value that may or may not be present.
+
+ Where the value is =null=, that is taken as not being present.
+
+ #+BEGIN_SRC java
+ final Maybe just = Maybe.maybe(1);
+ final Maybe nothing = Maybe.maybe(null);
+ #+END_SRC
+
+***** =static Maybe just(T value)=
+
+ Create a Maybe for the value that is present.
+
+ The =value= must not be =null= or a =NullPointerException= will be thrown.
+ If you can't prove that the value won't be =null= you should use
+ =Maybe.maybe(value)= instead.
+
+ #+BEGIN_SRC java
+ final Maybe just = Maybe.just(1);
+ #+END_SRC
+
+***** =static Maybe nothing()=
+
+ Create a Maybe for a lack of a value.
+
+ #+BEGIN_SRC java
+ final Maybe nothing = Maybe.nothing();
+ #+END_SRC
+
+**** Instance Methods
+
+***** =Maybe filter(Predicate predicate)=
+
+ Filter a Maybe by the predicate, replacing with Nothing when it fails.
+
+ #+BEGIN_SRC java
+ final Maybe maybe = Maybe.maybe(getValue())
+ .filter(v -> v % 2 == 0);
+ #+END_SRC
+
+***** = Maybe map(Function f)=
+
+ Applies the function to the value within the Maybe, returning the result within another Maybe.
+
+ #+BEGIN_SRC java
+ final Maybe maybe = Maybe.maybe(getValue())
+ .map(v -> v * 100);
+ #+END_SRC
+
+***** = Maybe flatMap(Function> f)=
+
+ Applies the function to the value within the =Maybe=, resulting in another =Maybe=, then flattens the resulting =Maybe>= into =Maybe=.
+
+ Monad binder maps the Maybe into another Maybe using the binder method f
+
+ #+BEGIN_SRC java
+ final Maybe maybe = Maybe.maybe(getValue())
+ .flatMap(v -> Maybe.maybe(getValueFor(v)));
+ #+END_SRC
+
+***** =void match(Consumer just, Runnable nothing)=
+
+ Matches the Maybe, either just or nothing, and performs either the Consumer, for Just, or Runnable for nothing.
+
+ #+BEGIN_SRC java
+ Maybe.maybe(getValue())
+ .match(
+ just -> workWithValue(just),
+ () -> nothingToWorkWith()
+ );
+ #+END_SRC
+
+***** =T orElse(T otherValue)=
+
+ A value to use when Maybe is Nothing.
+
+ #+BEGIN_SRC java
+ final Integer value = Maybe.maybe(getValue())
+ .orElse(1);
+ #+END_SRC
+
+***** =T orElseGet(Supplier otherValueSupplier)=
+
+ Provide a value to use when Maybe is Nothing.
+
+ #+BEGIN_SRC java
+ final Integer value = Maybe.maybe(getValue())
+ .orElseGet(() -> getDefaultValue());
+ #+END_SRC
+
+***** =void orElseThrow(Supplier error)=
+
+ Throw the exception if the Maybe is a Nothing.
+
+ #+BEGIN_SRC java
+ final Integer value = Maybe.maybe(getValue())
+ .orElseThrow(() -> new RuntimeException("error"));
+ #+END_SRC
+
+***** =Maybe peek(Consumer consumer)=
+
+ Provide the value within the Maybe, if it exists, to the Consumer, and returns this Maybe. Conceptually equivalent to the idea of =ifPresent(...)=.
+
+ #+BEGIN_SRC java
+ final Maybe maybe = Maybe.maybe(getValue())
+ .peek(v -> v.foo());
+ #+END_SRC
+
+***** =void ifNothing(Runnable runnable)=
+
+ Run the runnable if the Maybe is a Nothing, otherwise do nothing.
+
+ #+BEGIN_SRC java
+ Maybe.maybe(getValue())
+ .ifNothing(() -> doSomething());
+ #+END_SRC
+
+***** =Stream stream()=
+
+ Converts the Maybe into either a single value stream or an empty stream.
+
+ #+BEGIN_SRC java
+ final Stream stream = Maybe.maybe(getValue())
+ .stream();
+ #+END_SRC
+
+***** =boolean isJust()=
+
+ Checks if the Maybe is a Just.
+
+ #+BEGIN_SRC java
+ final boolean isJust = Maybe.maybe(getValue())
+ .isJust();
+ #+END_SRC
+
+***** =boolean isNothing()=
+
+ Checks if the Maybe is Nothing.
+
+ #+BEGIN_SRC java
+ final boolean isNothing = Maybe.maybe(getValue())
+ .isNothing();
+ #+END_SRC
+
+***** =Optional toOptional()=
+
+ Convert the Maybe to an Optional.
+
+ #+BEGIN_SRC java
+ final Optional optional = Maybe.maybe(getValue())
+ .toOptional();
+ #+END_SRC
+
+*** Result
+ :PROPERTIES:
+ :CUSTOM_ID: result
+ :END:
+
+ Allows handling error conditions without the need to catch exceptions.
+
+ When a =Result= is returned from a method it will contain one of two values.
+ Either the actual result, or an error in the form of an =Exception=. The
+ exception is returned within the =Result= and is not thrown.
+
+ #+BEGIN_SRC java
+ import net.kemitix.mon.result.Result;
+
+ import java.io.IOException;
+
+ class ResultExample implements Runnable {
+
+ public static void main(final String[] args) {
+ new ResultExample().run();
+ }
+
+ @Override
+ public void run() {
+ Result.of(() -> callRiskyMethod())
+ .flatMap(state -> doSomething(state))
+ .match(
+ success -> System.out.println(success),
+ error -> error.printStackTrace()
+ );
+ }
+
+ private String callRiskyMethod() throws IOException {
+ return "I'm fine";
+ }
+
+ private Result doSomething(final String state) {
+ return Result.of(() -> state + ", it's all good.");
+ }
+
+ }
+ #+END_SRC
+
+ In the above example the string ="I'm fine"= is returned by
+ =callRiskyMethod()= within a successful =Result=. The =.flatMap()= call,
+ unwraps that =Result= and, as it is a success, passes the contents to
+ =doSomething()=, which in turn returns a =Result= that the =.flatMap()= call
+ returns. =match()= is called on the =Result= and, being a success, will call
+ the success =Consumer=.
+
+ Had =callRiskyMethod()= thrown an exception it would have been caught by the
+ =Result.of()= method which would have then been an error =Result=. An error
+ Result would have ignored the =flatMap= and skipped to the =match()= when it
+ would have called the error =Consumer=.
+
+**** =Result= is a Monad
+
+ #+BEGIN_SRC java
+ package net.kemitix.mon;
+
+ import net.kemitix.mon.result.Result;
+ import org.assertj.core.api.WithAssertions;
+ import org.junit.Test;
+
+ import java.util.function.Function;
+
+ public class ResultMonadTest implements WithAssertions {
+
+ private final int v = 1;
+ private final Function> f = i -> r(i * 2);
+ private final Function> g = i -> r(i + 6);
+
+ private static Result r(int v) {
+ return Result.ok(v);
+ }
+
+ @Test
+ public void leftIdentity() {
+ assertThat(
+ r(v).flatMap(f)
+ ).isEqualTo(
+ f.apply(v)
+ );
+ }
+
+ @Test
+ public void rightIdentity() {
+ assertThat(
+ r(v).flatMap(x -> r(x))
+ ).isEqualTo(
+ r(v)
+ );
+ }
+
+ @Test
+ public void associativity() {
+ assertThat(
+ r(v).flatMap(f).flatMap(g)
+ ).isEqualTo(
+ r(v).flatMap(x -> f.apply(x).flatMap(g))
+ );
+ }
+
+ }
+ #+END_SRC
+
+**** Static Constructors
+
+***** =static Result of(Callable callable)=
+
+ Create a Result for a output of the Callable.
+
+ If the Callable throws and Exception, then the Result will be an error and
+ will contain that exception.
+
+ This will be the main starting point for most Results where the callable
+ could throw an =Exception=.
+
+ #+BEGIN_SRC java
+ final Result okay = Result.of(() -> 1);
+ final Result error = Result.of(() -> {throw new RuntimeException();});
+ #+END_SRC
+
+***** =static Result ok(T value)=
+
+ Create a Result for a success.
+
+ Use this where you have a value that you want to place into the Result context.
+
+ #+BEGIN_SRC java
+ final Result okay = Result.ok(1);
+ #+END_SRC
+
+***** =static Result error(Throwable error)=
+
+ Create a Result for an error.
+
+ #+BEGIN_SRC java
+ final Result error = Result.error(new RuntimeException());
+ #+END_SRC
+
+**** Static Methods
+
+ These static methods provide integration with the =Maybe= class.
+
+ #+BEGIN_SRC java
+ #+END_SRC
+
+***** =static Maybe toMaybe(Result result)=
+
+ Creates a =Maybe= from the =Result=, where the =Result= is a success, then
+ the =Maybe= will contain the value. However, if the =Result= is an error
+ then the =Maybe= will be nothing.
+
+ #+BEGIN_SRC java
+ final Result result = Result.of(() -> getValue());
+ final Maybe maybe = Result.toMaybe(result);
+ #+END_SRC
+
+***** =static Result fromMaybe(Maybe maybe, Supplier error)=
+
+ Creates a =Result= from the =Maybe=, where the =Result= will be an error
+ if the =Maybe= is nothing. Where the =Maybe= is nothing, then the
+ =Supplier= will provide the error for the =Result=.
+
+ #+BEGIN_SRC java
+ final Maybe maybe = Maybe.maybe(getValue());
+ final Result result = Result.fromMaybe(maybe, () -> new NoSuchFileException("filename"));
+ #+END_SRC
+
+***** =static Result> invert(Maybe> maybeResult)=
+
+ Swaps the =Result= within a =Maybe=, so that =Result= contains a =Maybe=.
+
+ #+BEGIN_SRC java
+ final Maybe> maybe = Maybe.maybe(Result.of(() -> getValue()));
+ final Result> result = Result.invert(maybe);
+ #+END_SRC
+
+***** =static Result> flatMapMaybe(Result> maybeResult, Function,Result>> f)=
+
+ Applies the function to the contents of a Maybe within the Result.
+
+ #+BEGIN_SRC java
+ final Result> result = Result.of(() -> Maybe.maybe(getValue()));
+ final Result> maybeResult = Result.flatMapMaybe(result, maybe -> Result.of(() -> maybe.map(v -> v * 2)));
+ #+END_SRC
+
+**** Instance Methods
+
+***** Result map(Function f)
+
+ Applies the function to the value within the Functor, returning the result
+ within a Functor.
+
+ #+BEGIN_SRC java
+ final Result result = Result.of(() -> getValue())
+ .map(v -> String.valueOf(v));
+ #+END_SRC
+
+***** Result flatMap(Function> f)
+
+ Returns a new Result consisting of the result of applying the function to
+ the contents of the Result.
+
+ #+BEGIN_SRC java
+ final Result result = Result.of(() -> getValue())
+ .flatMap(v -> Result.of(() -> String.valueOf(v)));
+ #+END_SRC
+
+***** Result andThen(Function> f)
+
+ Maps a Success Result to another Result using a Callable that is able to
+ throw a checked exception.
+
+ #+BEGIN_SRC java
+ final Result result = Result.of(() -> getValue())
+ .andThen(v -> () -> {throw new IOException();});
+ #+END_SRC
+
+***** void match(Consumer onSuccess, Consumer onError)
+
+ Matches the Result, either success or error, and supplies the appropriate
+ Consumer with the value or error.
+
+ #+BEGIN_SRC java
+ Result.of(() -> getValue())
+ .match(
+ success -> System.out.println(success),
+ error -> System.err.println("error")
+ );
+ #+END_SRC
+
+***** Result recover(Function> f)
+
+ Provide a way to attempt to recover from an error state.
+
+ #+BEGIN_SRC java
+ final Result result = Result.of(() -> getValue())
+ .recover(e -> Result.of(() -> getSafeValue(e)));
+ #+END_SRC
+
+***** Result peek(Consumer consumer)
+
+ Provide the value within the Result, if it is a success, to the Consumer,
+ and returns this Result.
+
+ #+BEGIN_SRC java
+ final Result result = Result.of(() -> getValue())
+ .peek(v -> System.out.println(v));
+ #+END_SRC
+
+***** Result thenWith(Function> f)
+
+ Perform the continuation with the current Result value then return the
+ current Result, assuming there was no error in the continuation.
+
+ #+BEGIN_SRC java
+ final Result result = Result.of(() -> getValue())
+ .thenWith(v -> () -> System.out.println(v))
+ .thenWith(v -> () -> {throw new IOException();});
+ #+END_SRC
+
+***** Result> maybe(Predicate predicate)
+
+ Wraps the value within the Result in a Maybe, either a Just if the
+ predicate is true, or Nothing.
+
+ #+BEGIN_SRC java
+ final Result> result = Result.of(() -> getValue())
+ .maybe(v -> v % 2 == 0);
+ #+END_SRC
+
+***** T orElseThrow()
+
+ Extracts the successful value from the result, or throws the error
+ Throwable.
+
+ #+BEGIN_SRC java
+ final Integer result = Result.of(() -> getValue())
+ .orElseThrow();
+ #+END_SRC
+
+***** void onError(Consumer errorConsumer)
+
+ A handler for error states.
+
+ #+BEGIN_SRC java
+ Result.of(() -> getValue())
+ .onError(e -> handleError(e));
+ #+END_SRC
+
+***** boolean isOkay()
+
+ Checks if the Result is a success.
+
+ #+BEGIN_SRC java
+ final boolean isOkay = Result.of(() -> getValue())
+ .isOkay();
+ #+END_SRC
+
+***** boolean isError()
+
+ Checks if the Result is an error.
+
+ #+BEGIN_SRC java
+ final boolean isError = Result.of(() -> getValue())
+ .isError();
+ #+END_SRC
diff --git a/src/main/java/net/kemitix/mon/maybe/Just.java b/src/main/java/net/kemitix/mon/maybe/Just.java
index 9dea530..fd916bf 100644
--- a/src/main/java/net/kemitix/mon/maybe/Just.java
+++ b/src/main/java/net/kemitix/mon/maybe/Just.java
@@ -97,6 +97,11 @@ final class Just implements Maybe {
return Optional.of(value);
}
+ @Override
+ public T orElseThrow(final Supplier extends X> e) throws X {
+ return value;
+ }
+
@Override
public Maybe peek(final Consumer consumer) {
consumer.accept(value);
@@ -113,11 +118,6 @@ final class Just implements Maybe {
justMatcher.accept(value);
}
- @Override
- public void orElseThrow(final Supplier e) {
- // do not throw
- }
-
@Override
public Stream stream() {
return Stream.of(value);
diff --git a/src/main/java/net/kemitix/mon/maybe/Maybe.java b/src/main/java/net/kemitix/mon/maybe/Maybe.java
index 0093c53..e24dec7 100644
--- a/src/main/java/net/kemitix/mon/maybe/Maybe.java
+++ b/src/main/java/net/kemitix/mon/maybe/Maybe.java
@@ -43,6 +43,9 @@ public interface Maybe extends Functor> {
/**
* Create a Maybe for the value that is present.
*
+ *
The {@literal value} must not be {@literal null} or a {@literal NullPointerException} will be thrown.
+ * If you can't prove that the value won't be {@literal null} you should use {@link #maybe(Object)} instead.
+ *
* @param value the value, not null
* @param the type of the value
* @return a Maybe of the value
@@ -65,6 +68,8 @@ public interface Maybe extends Functor> {
/**
* Create a Maybe for the value that may or may not be present.
*
+ *
Where the value is {@literal null}, that is taken as not being present.
+ *
* @param value the value, may be null
* @param the type of the value
* @return a Maybe, either a Just, or Nothing if value is null
@@ -114,7 +119,7 @@ public interface Maybe extends Functor> {
* A value to use when Maybe is Nothing.
*
* @param otherValue an alternate value
- * @return a Maybe
+ * @return the value of the Maybe if a Just, otherwise the otherValue
*/
T orElse(T otherValue);
@@ -128,14 +133,15 @@ public interface Maybe extends Functor> {
/**
* Throw the exception if the Maybe is a Nothing.
*
- * @param e the exception to throw
- * @throws Exception if the Maybe is a Nothing
+ * @param e the exception to throw
+ * @param the type of the exception to throw
+ * @return the value of the Maybe if a Just
+ * @throws X if the Maybe is nothing
*/
- @SuppressWarnings("illegalthrows")
- void orElseThrow(Supplier e) throws Exception;
+ T orElseThrow(Supplier extends X> e) throws X;
/**
- * Converts the Maybe into either a single value stream or and empty stream.
+ * Converts the Maybe into either a single value stream or an empty stream.
*
* @return a Stream containing the value or nothing.
*/
diff --git a/src/main/java/net/kemitix/mon/maybe/Nothing.java b/src/main/java/net/kemitix/mon/maybe/Nothing.java
index 58f169d..ada237e 100644
--- a/src/main/java/net/kemitix/mon/maybe/Nothing.java
+++ b/src/main/java/net/kemitix/mon/maybe/Nothing.java
@@ -80,6 +80,11 @@ final class Nothing implements Maybe {
return Optional.empty();
}
+ @Override
+ public T orElseThrow(final Supplier extends X> e) throws X {
+ throw e.get();
+ }
+
@Override
public Maybe peek(final Consumer consumer) {
return this;
@@ -95,11 +100,6 @@ final class Nothing implements Maybe {
nothingMatcher.run();
}
- @Override
- public void orElseThrow(final Supplier e) throws Exception {
- throw e.get();
- }
-
@Override
public Stream stream() {
return Stream.empty();
diff --git a/src/main/java/net/kemitix/mon/result/Result.java b/src/main/java/net/kemitix/mon/result/Result.java
index 72cafdb..4240f67 100644
--- a/src/main/java/net/kemitix/mon/result/Result.java
+++ b/src/main/java/net/kemitix/mon/result/Result.java
@@ -91,7 +91,9 @@ public interface Result extends Functor> {
}
/**
- * Creates a Result from the Maybe, where the Result will be an error if the Maybe is Nothing.
+ * Creates a {@link Maybe} from the Result, where the Result is a success, then the Maybe will contain the value.
+ *
+ *
However, if the Result is an error then the Maybe will be nothing.
*
* @param result the Result the might contain the value of the Result
* @param the type of the Maybe and the Result
diff --git a/src/test/java/net/kemitix/mon/MaybeMonadTest.java b/src/test/java/net/kemitix/mon/MaybeMonadTest.java
index 28e9e8b..bbb6618 100644
--- a/src/test/java/net/kemitix/mon/MaybeMonadTest.java
+++ b/src/test/java/net/kemitix/mon/MaybeMonadTest.java
@@ -8,32 +8,39 @@ import java.util.function.Function;
public class MaybeMonadTest implements WithAssertions {
+ private final int v = 1;
+ private final Function> f = i -> m(i * 2);
+ private final Function> g = i -> m(i + 6);
+
+ private static Maybe m(int value) {
+ return Maybe.maybe(value);
+ }
+
@Test
public void leftIdentity() {
- //given
- final int value = 1;
- final Maybe maybe = Maybe.maybe(value);
- final Function> f = i -> Maybe.maybe(i * 2);
- //then
- assertThat(maybe.flatMap(f)).isEqualTo(f.apply(value));
+ assertThat(
+ m(v).flatMap(f)
+ ).isEqualTo(
+ f.apply(v)
+ );
}
@Test
public void rightIdentity() {
- //given
- final Maybe maybe = Maybe.maybe(1);
- //then
- assertThat(maybe.flatMap(Maybe::maybe)).isEqualTo(maybe);
+ assertThat(
+ m(v).flatMap(x -> m(x))
+ ).isEqualTo(
+ m(v)
+ );
}
@Test
public void associativity() {
- //given
- final Maybe maybe = Maybe.maybe(1);
- final Function> f = i -> Maybe.maybe(i * 2);
- final Function> g = i -> Maybe.maybe(i + 6);
- //then
- assertThat(maybe.flatMap(f).flatMap(g)).isEqualTo(maybe.flatMap(x -> f.apply(x).flatMap(g)));
+ assertThat(
+ m(v).flatMap(f).flatMap(g)
+ ).isEqualTo(
+ m(v).flatMap(x -> f.apply(x).flatMap(g))
+ );
}
}
diff --git a/src/test/java/net/kemitix/mon/MaybeTest.java b/src/test/java/net/kemitix/mon/MaybeTest.java
index 74c6efe..ae354fc 100644
--- a/src/test/java/net/kemitix/mon/MaybeTest.java
+++ b/src/test/java/net/kemitix/mon/MaybeTest.java
@@ -51,6 +51,29 @@ public class MaybeTest implements WithAssertions {
assertThat(nothing().map(v -> v)).isEqualTo(nothing());
}
+ @Test
+ public void mapToNull_thenJustNull() {
+ //given
+ final Maybe maybe = just(1);
+ //when
+ final Maybe