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) [![Jenkins](https://img.shields.io/jenkins/s/https/jenkins.kemitix.net/job/GitLab/job/kemitix%252Fmon.svg?style=for-the-badge)](https://jenkins.kemitix.net/job/GitLab/job/kemitix%252Fmon/) [![Jenkins tests](https://img.shields.io/jenkins/t/https/jenkins.kemitix.net/job/GitLab/job/kemitix%252Fmon.svg?style=for-the-badge)](https://jenkins.kemitix.net/job/GitLab/job/kemitix%252Fmon/) [![Jenkins coverage](https://img.shields.io/jenkins/c/https/jenkins.kemitix.net/job/GitLab/job/kemitix%252Fmon.svg?style=for-the-badge)](https://jenkins.kemitix.net/job/GitLab/job/kemitix%252Fmon/) [![Codacy grade](https://img.shields.io/codacy/grade/d57096b0639d496aba9a7e43e7cf5b4c.svg?style=for-the-badge)](https://app.codacy.com/project/kemitix/mon/dashboard) ## Maven ```xml net.kemitix mon 0.7.0 ``` 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 ```