From 85a76c4d1df3ff1eafd8bd9257d4f4ea2506e69a Mon Sep 17 00:00:00 2001 From: Glavo Date: Mon, 23 Oct 2023 02:44:23 +0800 Subject: [PATCH] Close #71: Create Either::bifold --- .../src/main/java/kala/control/Either.java | 984 +++++++++--------- 1 file changed, 510 insertions(+), 474 deletions(-) diff --git a/kala-base/src/main/java/kala/control/Either.java b/kala-base/src/main/java/kala/control/Either.java index ce600e2c..ddfe6a7c 100644 --- a/kala-base/src/main/java/kala/control/Either.java +++ b/kala-base/src/main/java/kala/control/Either.java @@ -1,474 +1,510 @@ -package kala.control; - -import kala.annotations.Covariant; -import kala.annotations.Sealed; -import kala.annotations.UnstableName; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; - -import java.io.Serializable; -import java.util.NoSuchElementException; -import java.util.Objects; -import java.util.function.Consumer; -import java.util.function.Function; - -@Sealed(subclasses = {Either.Left.class, Either.Right.class}) -@SuppressWarnings("unchecked") -public abstract class Either<@Covariant A, @Covariant B> implements Serializable { - Either() { - } - - @Contract(value = "_ -> param1", pure = true) - public static Either narrow(Either either) { - return (Either) either; - } - - @Contract(value = "_ -> param1", pure = true) - public static Either.Left narrow(Either.Left left) { - return (Either.Left) left; - } - - @Contract(value = "_ -> param1", pure = true) - public static Either.Right narrow(Either.Right right) { - return (Either.Right) right; - } - - @Contract("_ -> new") - public static Either.@NotNull Left left(A value) { - return new Left<>(value); - } - - @Contract("_ -> new") - public static Either.@NotNull Right right(B value) { - return new Right<>(value); - } - - @UnstableName - public static T join(@NotNull Either either) { - if (either.isLeft()) { - return either.getLeftValue(); - } else { - return either.getRightValue(); - } - } - - public abstract boolean isLeft(); - - public abstract boolean isRight(); - - public abstract A getLeftValue(); - - public abstract @NotNull Option getLeftOption(); - - public abstract B getRightValue(); - - public abstract @NotNull Option getRightOption(); - - public abstract @NotNull Either map( - @NotNull Function leftMapper, - @NotNull Function rightMapper - ); - - public abstract @NotNull Either mapLeft(@NotNull Function mapper); - - public abstract @NotNull Either mapRight(@NotNull Function mapper); - - public abstract U fold( - @NotNull Function leftMapper, - @NotNull Function rightMapper - ); - - @Contract("-> new") - public abstract @NotNull Either swap(); - - @Contract("-> new") - public abstract @NotNull Result toResult(); - - @Contract(" -> new") - public final @NotNull LeftProjection leftProjection() { - return this.new LeftProjection(); - } - - @Contract(" -> new") - public final @NotNull RightProjection rightProjection() { - return this.new RightProjection(); - } - - public abstract void forEach( - @NotNull Consumer leftConsumer, - @NotNull Consumer rightConsumer - ); - - public final static class Left<@Covariant A, @Covariant B> extends Either { - private static final long serialVersionUID = -1160729620210301179L; - - private static final int HASH_MAGIC = -1951578063; - - private final A value; - - Left(A value) { - this.value = value; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isLeft() { - return true; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isRight() { - return false; - } - - /** - * {@inheritDoc} - */ - @Override - public A getLeftValue() { - return value; - } - - /** - * {@inheritDoc} - */ - @Override - public @NotNull Option getLeftOption() { - return Option.some(value); - } - - /** - * {@inheritDoc} - */ - @Override - @Contract("-> fail") - public B getRightValue() { - throw new NoSuchElementException("Either.Left.getRightValue"); - } - - /** - * {@inheritDoc} - */ - @Override - public @NotNull Option getRightOption() { - return Option.none(); - } - - @Override - public @NotNull Either map( - @NotNull Function leftMapper, - @NotNull Function rightMapper) { - return Either.left(leftMapper.apply(value)); - } - - /** - * {@inheritDoc} - */ - @Override - public @NotNull Either mapLeft(@NotNull Function mapper) { - Objects.requireNonNull(mapper); - return Either.left(mapper.apply(value)); - } - - /** - * {@inheritDoc} - */ - @Override - public @NotNull Either mapRight(@NotNull Function mapper) { - return (Either) this; - } - - @Override - public U fold( - @NotNull Function leftMapper, - @NotNull Function rightMapper) { - return leftMapper.apply(value); - } - - /** - * {@inheritDoc} - */ - @Override - public @NotNull Either swap() { - return Either.right(value); - } - - @Override - public @NotNull Result toResult() { - return Result.err(value); - } - - @Override - public void forEach(@NotNull Consumer leftConsumer, @NotNull Consumer rightConsumer) { - leftConsumer.accept(value); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof Left)) { - return false; - } - - return Objects.equals(value, ((Left) obj).value); - } - - /** - * {@inheritDoc} - */ - @Override - public int hashCode() { - return Objects.hashCode(value) + HASH_MAGIC; - } - - /** - * {@inheritDoc} - */ - @Override - public String toString() { - return "Either.Left[" + value + "]"; - } - } - - public final static class Right<@Covariant A, @Covariant B> extends Either { - private static final long serialVersionUID = -3372589401685464421L; - - private static final int HASH_MAGIC = 1973604283; - - private final B value; - - Right(B value) { - this.value = value; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isLeft() { - return false; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isRight() { - return true; - } - - /** - * {@inheritDoc} - */ - @Override - @Contract("-> fail") - public A getLeftValue() { - throw new NoSuchElementException("Either.Right.getLeftValue"); - } - - /** - * {@inheritDoc} - */ - @Override - public @NotNull Option getLeftOption() { - return Option.none(); - } - - /** - * {@inheritDoc} - */ - @Override - public B getRightValue() { - return value; - } - - /** - * {@inheritDoc} - */ - @Override - public @NotNull Option getRightOption() { - return Option.some(value); - } - - @Override - public @NotNull Either map( - @NotNull Function leftMapper, - @NotNull Function rightMapper - ) { - return Either.right(rightMapper.apply(value)); - } - - /** - * {@inheritDoc} - */ - @Override - public @NotNull Either mapLeft(@NotNull Function mapper) { - return (Either) this; - } - - /** - * {@inheritDoc} - */ - @Override - public @NotNull Either mapRight(@NotNull Function mapper) { - Objects.requireNonNull(mapper); - return Either.right(mapper.apply(value)); - } - - @Override - public U fold( - @NotNull Function leftMapper, - @NotNull Function rightMapper) { - return rightMapper.apply(value); - } - - /** - * {@inheritDoc} - */ - @Override - public @NotNull Either swap() { - return Either.left(value); - } - - @Override - public @NotNull Result toResult() { - return Result.ok(value); - } - - @Override - public void forEach(@NotNull Consumer leftConsumer, @NotNull Consumer rightConsumer) { - rightConsumer.accept(value); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof Right)) { - return false; - } - - return Objects.equals(value, ((Right) obj).value); - } - - /** - * {@inheritDoc} - */ - @Override - public int hashCode() { - return Objects.hashCode(value) + HASH_MAGIC; - } - - /** - * {@inheritDoc} - */ - @Override - public String toString() { - return "Either.Right[" + value + "]"; - } - } - - public abstract class Projection<@Covariant T> implements OptionContainer { - Projection() { - } - - private static final int HASH_MAGIC = 905770825; - - public final @NotNull Either getEither() { - return Either.this; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - return Either.this.equals(((Projection) o).getEither()); - } - - @Override - public final int hashCode() { - return Either.this.hashCode() + HASH_MAGIC; - } - } - - public final class LeftProjection extends Projection { - @Override - public boolean isDefined() { - return Either.this.isLeft(); - } - - @Override - public A get() { - return Either.this.getLeftValue(); - } - - @Override - @NotNull - public Either.LeftProjection map(@NotNull Function mapper) { - Objects.requireNonNull(mapper); - if (isDefined()) { - return Either.left(mapper.apply(getLeftValue())).new LeftProjection(); - } - return (Either.LeftProjection) this; - } - - /** - * {@inheritDoc} - */ - @NotNull - @Override - public String toString() { - return Either.this + ".LeftProjection"; - } - } - - public final class RightProjection extends Projection { - @Override - public boolean isDefined() { - return Either.this.isRight(); - } - - @Override - public B get() { - return Either.this.getRightValue(); - } - - @Override - @NotNull - public Either.RightProjection map(@NotNull Function mapper) { - Objects.requireNonNull(mapper); - if (isDefined()) { - return Either.right(mapper.apply(getRightValue())).new RightProjection(); - } - return (Either.RightProjection) this; - } - - /** - * {@inheritDoc} - */ - @NotNull - @Override - public String toString() { - return Either.this + ".RightProjection"; - } - } -} +package kala.control; + +import kala.annotations.Covariant; +import kala.annotations.Sealed; +import kala.annotations.UnstableName; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.io.Serializable; +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.Function; + +@Sealed(subclasses = {Either.Left.class, Either.Right.class}) +@SuppressWarnings("unchecked") +public abstract class Either<@Covariant A, @Covariant B> implements Serializable { + Either() { + } + + @Contract(value = "_ -> param1", pure = true) + public static Either narrow(Either either) { + return (Either) either; + } + + @Contract(value = "_ -> param1", pure = true) + public static Either.Left narrow(Either.Left left) { + return (Either.Left) left; + } + + @Contract(value = "_ -> param1", pure = true) + public static Either.Right narrow(Either.Right right) { + return (Either.Right) right; + } + + @Contract("_ -> new") + public static Either.@NotNull Left left(A value) { + return new Left<>(value); + } + + @Contract("_ -> new") + public static Either.@NotNull Right right(B value) { + return new Right<>(value); + } + + @UnstableName + public static T join(@NotNull Either either) { + if (either.isLeft()) { + return either.getLeftValue(); + } else { + return either.getRightValue(); + } + } + + public abstract boolean isLeft(); + + public abstract boolean isRight(); + + public abstract A getLeftValue(); + + public abstract @NotNull Option getLeftOption(); + + public abstract B getRightValue(); + + public abstract @NotNull Option getRightOption(); + + public abstract @NotNull Either map( + @NotNull Function leftMapper, + @NotNull Function rightMapper + ); + + public abstract @NotNull Either mapLeft(@NotNull Function mapper); + + public abstract @NotNull Either mapRight(@NotNull Function mapper); + + public abstract U fold( + @NotNull Function leftMapper, + @NotNull Function rightMapper + ); + + public abstract U bifold( + @NotNull Either other, + U defaultValue, + @NotNull BiFunction leftMapper, + @NotNull BiFunction rightMapper + ); + + @Contract("-> new") + public abstract @NotNull Either swap(); + + @Contract("-> new") + public abstract @NotNull Result toResult(); + + @Contract(" -> new") + public final @NotNull LeftProjection leftProjection() { + return this.new LeftProjection(); + } + + @Contract(" -> new") + public final @NotNull RightProjection rightProjection() { + return this.new RightProjection(); + } + + public abstract void forEach( + @NotNull Consumer leftConsumer, + @NotNull Consumer rightConsumer + ); + + public final static class Left<@Covariant A, @Covariant B> extends Either { + private static final long serialVersionUID = -1160729620210301179L; + + private static final int HASH_MAGIC = -1951578063; + + private final A value; + + Left(A value) { + this.value = value; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isLeft() { + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isRight() { + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public A getLeftValue() { + return value; + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull Option getLeftOption() { + return Option.some(value); + } + + /** + * {@inheritDoc} + */ + @Override + @Contract("-> fail") + public B getRightValue() { + throw new NoSuchElementException("Either.Left.getRightValue"); + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull Option getRightOption() { + return Option.none(); + } + + @Override + public @NotNull Either map( + @NotNull Function leftMapper, + @NotNull Function rightMapper) { + return Either.left(leftMapper.apply(value)); + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull Either mapLeft(@NotNull Function mapper) { + Objects.requireNonNull(mapper); + return Either.left(mapper.apply(value)); + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull Either mapRight(@NotNull Function mapper) { + return (Either) this; + } + + @Override + public U fold( + @NotNull Function leftMapper, + @NotNull Function rightMapper) { + return leftMapper.apply(value); + } + + @Override + public U bifold( + @NotNull Either other, + U defaultValue, + @NotNull BiFunction leftMapper, + @NotNull BiFunction rightMapper) { + + if (other instanceof Left) { + return leftMapper.apply(this.value, ((Left) other).value); + } else { + return defaultValue; + } + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull Either swap() { + return Either.right(value); + } + + @Override + public @NotNull Result toResult() { + return Result.err(value); + } + + @Override + public void forEach(@NotNull Consumer leftConsumer, @NotNull Consumer rightConsumer) { + leftConsumer.accept(value); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof Left)) { + return false; + } + + return Objects.equals(value, ((Left) obj).value); + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + return Objects.hashCode(value) + HASH_MAGIC; + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return "Either.Left[" + value + "]"; + } + } + + public final static class Right<@Covariant A, @Covariant B> extends Either { + private static final long serialVersionUID = -3372589401685464421L; + + private static final int HASH_MAGIC = 1973604283; + + private final B value; + + Right(B value) { + this.value = value; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isLeft() { + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isRight() { + return true; + } + + /** + * {@inheritDoc} + */ + @Override + @Contract("-> fail") + public A getLeftValue() { + throw new NoSuchElementException("Either.Right.getLeftValue"); + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull Option getLeftOption() { + return Option.none(); + } + + /** + * {@inheritDoc} + */ + @Override + public B getRightValue() { + return value; + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull Option getRightOption() { + return Option.some(value); + } + + @Override + public @NotNull Either map( + @NotNull Function leftMapper, + @NotNull Function rightMapper + ) { + return Either.right(rightMapper.apply(value)); + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull Either mapLeft(@NotNull Function mapper) { + return (Either) this; + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull Either mapRight(@NotNull Function mapper) { + Objects.requireNonNull(mapper); + return Either.right(mapper.apply(value)); + } + + @Override + public U fold( + @NotNull Function leftMapper, + @NotNull Function rightMapper) { + return rightMapper.apply(value); + } + + @Override + public U bifold( + @NotNull Either other, + U defaultValue, + @NotNull BiFunction leftMapper, + @NotNull BiFunction rightMapper) { + + if (other instanceof Right) { + return rightMapper.apply(this.value, ((Right) other).value); + } else { + return defaultValue; + } + } + + /** + * {@inheritDoc} + */ + @Override + public @NotNull Either swap() { + return Either.left(value); + } + + @Override + public @NotNull Result toResult() { + return Result.ok(value); + } + + @Override + public void forEach(@NotNull Consumer leftConsumer, @NotNull Consumer rightConsumer) { + rightConsumer.accept(value); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof Right)) { + return false; + } + + return Objects.equals(value, ((Right) obj).value); + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + return Objects.hashCode(value) + HASH_MAGIC; + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return "Either.Right[" + value + "]"; + } + } + + public abstract class Projection<@Covariant T> implements OptionContainer { + Projection() { + } + + private static final int HASH_MAGIC = 905770825; + + public final @NotNull Either getEither() { + return Either.this; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + return Either.this.equals(((Projection) o).getEither()); + } + + @Override + public final int hashCode() { + return Either.this.hashCode() + HASH_MAGIC; + } + } + + public final class LeftProjection extends Projection { + @Override + public boolean isDefined() { + return Either.this.isLeft(); + } + + @Override + public A get() { + return Either.this.getLeftValue(); + } + + @Override + @NotNull + public Either.LeftProjection map(@NotNull Function mapper) { + Objects.requireNonNull(mapper); + if (isDefined()) { + return Either.left(mapper.apply(getLeftValue())).new LeftProjection(); + } + return (Either.LeftProjection) this; + } + + /** + * {@inheritDoc} + */ + @NotNull + @Override + public String toString() { + return Either.this + ".LeftProjection"; + } + } + + public final class RightProjection extends Projection { + @Override + public boolean isDefined() { + return Either.this.isRight(); + } + + @Override + public B get() { + return Either.this.getRightValue(); + } + + @Override + @NotNull + public Either.RightProjection map(@NotNull Function mapper) { + Objects.requireNonNull(mapper); + if (isDefined()) { + return Either.right(mapper.apply(getRightValue())).new RightProjection(); + } + return (Either.RightProjection) this; + } + + /** + * {@inheritDoc} + */ + @NotNull + @Override + public String toString() { + return Either.this + ".RightProjection"; + } + } +}