package scotch.data.either; import static java.util.Arrays.asList; import static scotch.runtime.RuntimeSupport.applicable; import static scotch.runtime.RuntimeSupport.callable; import static scotch.symbol.type.TypeDescriptors.fn; import static scotch.symbol.type.TypeDescriptors.sum; import static scotch.symbol.type.TypeDescriptors.var; import java.util.List; import java.util.Objects; import scotch.runtime.Applicable; import scotch.runtime.Callable; import scotch.runtime.RuntimeSupport; import scotch.symbol.DataConstructor; import scotch.symbol.DataType; import scotch.symbol.TypeParameter; import scotch.symbol.TypeParameters; import scotch.symbol.Value; import scotch.symbol.ValueType; import scotch.symbol.type.TypeDescriptor; @SuppressWarnings("unused") @DataType(memberName = "Either", parameters = { @TypeParameter(name = "a"), @TypeParameter(name = "b"), }) public abstract class Either<A, B> { @Value(memberName = "Left") public static <A, B> Applicable<A, Either<A, B>> left() { return applicable(value -> callable(() -> new Left<>(value))); } public static <A, B> Either<A, B> left(A value) { return Either.<A, B>left().apply(RuntimeSupport.box(value)).call(); } @ValueType(forMember = "Left") public static TypeDescriptor left$type() { return fn(var("a"), sum("scotch.data.either.Either", var("a"), var("b"))); } @TypeParameters public static List<TypeDescriptor> parameters() { return asList(var("a"), var("b")); } @Value(memberName = "Right") public static <A, B> Applicable<B, Either<A, B>> right() { return applicable(value -> callable(() -> new Right<>(value))); } @ValueType(forMember = "Right") public static TypeDescriptor right$type() { return fn(var("b"), sum("scotch.data.either.Either", var("a"), var("b"))); } private Either() { // intentionally empty } @Override public abstract boolean equals(Object o); @Override public abstract int hashCode(); public abstract <C> Either<A, C> map(Applicable<B, C> function); @Override public abstract String toString(); @DataConstructor(ordinal = 0, memberName = "Left", dataType ="Either") public static class Left<A, B> extends Either<A, B> { private final Callable<A> value; public Left(Callable<A> value) { this.value = value; } @Override public boolean equals(Object o) { return o == this || o instanceof Left && Objects.equals(value.call(), ((Left) o).value.call()); } @Override public int hashCode() { return Objects.hash(value.call()); } @SuppressWarnings("unchecked") @Override public <C> Either<A, C> map(Applicable<B, C> function) { return (Either<A, C>) this; } @Override public String toString() { return "Left(" + value.call() + ")"; } } @DataConstructor(ordinal = 1, memberName = "Right", dataType = "Either") public static class Right<A, B> extends Either<A, B> { private final Callable<B> value; public Right(Callable<B> value) { this.value = value; } @Override public boolean equals(Object o) { return o == this || o instanceof Right && Objects.equals(value.call(), ((Right) o).value.call()); } @Override public int hashCode() { return Objects.hash(value.call()); } @Override public <C> Either<A, C> map(Applicable<B, C> function) { return new Right<>(function.apply(value)); } @Override public String toString() { return "Right(" + value.call() + ")"; } } }