mon/README.md

7.4 KiB

Mon

TypeAlias, Maybe and Result for Java

Sonatype Nexus (Releases) Maven Central

SonarQube Coverage SonarQube Tech Debt

Jenkins Jenkins tests Jenkins coverage

Codacy grade

Maven

<dependency>
    <groupId>net.kemitix</groupId>
    <artifactId>mon</artifactId>
    <version>RELEASE</version>
</dependency>

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.

class Goal extends TypeAlias<String> {
    private Goal(final String goal) {
        super(goal);
    }
    public static Goal of(final String goal) {
        return new Goal(goal);
    }
}
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.

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<T>)
        assertThat(Maybe.nothing().orElseGet(() -> 1)).isEqualTo(1);
        assertThat(Maybe.just(1).orElseGet(() -> 2)).isEqualTo(1);
        // .orElse(Supplier<T>)
        assertThat(Maybe.nothing().orElse(1)).isEqualTo(1);
        assertThat(Maybe.just(1).orElse(2)).isEqualTo(1);
        // .filter(Predicate<T>)
        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<T>) 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<T>)
        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<T>)
        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<Exception>)
        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.

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<Integer> 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<Integer> badResult = badMethod();
        badResult.match(
                success -> notCalled(success),
                error -> handleError(error)
        );
    }

    private Result<Integer> 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<String> convertToString(final Integer number) {
        System.out.println("convertToString");
        return Result.ok(String.valueOf(number));
    }

    private Result<Integer> 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<Integer> 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:

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