package scotch.data.maybe; import static java.util.Arrays.asList; import static scotch.runtime.RuntimeSupport.applicable; import static scotch.runtime.RuntimeSupport.callable; import static scotch.runtime.RuntimeSupport.flatCallable; 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.DataField; import scotch.symbol.DataFieldType; 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 = "Maybe", parameters = { @TypeParameter(name = "a"), }) public abstract class Maybe<A> { public static final TypeDescriptor TYPE = sum("scotch.data.maybe.Maybe", var("a")); private static final Callable<Maybe> NOTHING = callable(Nothing::new); @Value(memberName = "Just") public static <A> Applicable<A, Maybe<A>> just() { return applicable(value -> callable(() -> new Just<>(value))); } @SuppressWarnings("unchecked") public static <A> Maybe<A> just(A value) { return (Maybe<A>) just().apply(RuntimeSupport.box(value)).call(); } @ValueType(forMember = "Just") public static TypeDescriptor just$type() { return fn(var("a"), TYPE); } @SuppressWarnings("unchecked") @Value(memberName = "Nothing") public static <A> Callable<Maybe<A>> nothing() { return (Callable) NOTHING; } @ValueType(forMember = "Nothing") public static TypeDescriptor nothing$type() { return TYPE; } @TypeParameters public static List<TypeDescriptor> parameters() { return asList(var("a")); } private Maybe() { // intentionally empty } public abstract boolean equals(Object o); public abstract int hashCode(); public abstract <B> Callable<Maybe<B>> map(Applicable<A, Maybe<B>> function); public abstract String toString(); @DataConstructor(ordinal = 0, memberName = "Nothing", dataType = "Maybe") public static class Nothing<A> extends Maybe<A> { @Override public boolean equals(Object o) { return o == this || o instanceof Nothing; } @Override public int hashCode() { return Objects.hash(17); } @Override public <B> Callable<Maybe<B>> map(Applicable<A, Maybe<B>> function) { return nothing(); } @Override public String toString() { return "Nothing"; } } @DataConstructor(ordinal = 1, memberName = "Just", dataType = "Maybe") public static class Just<A> extends Maybe<A> { @DataFieldType(forMember = "value") public static TypeDescriptor value$type() { return var("a"); } private final Callable<A> value; private Just(Callable<A> value) { this.value = value; } @Override public boolean equals(Object o) { return o == this || o instanceof Just && Objects.equals(value.call(), ((Just) o).value.call()); } @DataField(memberName = "value", ordinal = 0) public Callable<A> getValue() { return value; } @Override public int hashCode() { return Objects.hash(value); } @Override public <B> Callable<Maybe<B>> map(Applicable<A, Maybe<B>> function) { return flatCallable(() -> function.apply(value)); } @Override public String toString() { return "Just " + value.call() + ""; } } }