All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.jnape.palatable.lambda.adt.choice.Choice3 Maven / Gradle / Ivy

There is a newer version: 5.5.0
Show newest version
package com.jnape.palatable.lambda.adt.choice;

import com.jnape.palatable.lambda.adt.Maybe;
import com.jnape.palatable.lambda.adt.coproduct.CoProduct2;
import com.jnape.palatable.lambda.adt.coproduct.CoProduct3;
import com.jnape.palatable.lambda.adt.hlist.HList;
import com.jnape.palatable.lambda.adt.hlist.Tuple3;
import com.jnape.palatable.lambda.functions.Fn1;
import com.jnape.palatable.lambda.functions.recursion.RecursiveResult;
import com.jnape.palatable.lambda.functions.specialized.Pure;
import com.jnape.palatable.lambda.functor.Applicative;
import com.jnape.palatable.lambda.functor.Bifunctor;
import com.jnape.palatable.lambda.functor.Functor;
import com.jnape.palatable.lambda.functor.builtin.Lazy;
import com.jnape.palatable.lambda.monad.Monad;
import com.jnape.palatable.lambda.monad.MonadRec;
import com.jnape.palatable.lambda.traversable.Traversable;

import java.util.Objects;

import static com.jnape.palatable.lambda.functions.builtin.fn2.Into3.into3;
import static com.jnape.palatable.lambda.functions.recursion.RecursiveResult.terminate;
import static com.jnape.palatable.lambda.functions.recursion.Trampoline.trampoline;
import static com.jnape.palatable.lambda.functor.builtin.Lazy.lazy;

/**
 * Canonical ADT representation of {@link CoProduct3}.
 *
 * @param  the first possible type
 * @param  the second possible type
 * @param  the third possible type
 * @see Choice2
 * @see Choice4
 */
public abstract class Choice3 implements
        CoProduct3>,
        MonadRec>,
        Bifunctor>,
        Traversable> {

    private Choice3() {
    }

    /**
     * Specialize this choice's projection to a {@link Tuple3}.
     *
     * @return a {@link Tuple3}
     */
    @Override
    public Tuple3, Maybe, Maybe> project() {
        return into3(HList::tuple, CoProduct3.super.project());
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final  Choice4 diverge() {
        return match(Choice4::a, Choice4::b, Choice4::c);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final Choice2 converge(Fn1> convergenceFn) {
        return match(Choice2::a, Choice2::b, convergenceFn.fmap(cp2 -> cp2.match(Choice2::a, Choice2::b)));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final  Choice3 fmap(Fn1 fn) {
        return MonadRec.super.fmap(fn).coerce();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final  Choice3 biMapL(Fn1 fn) {
        return (Choice3) Bifunctor.super.biMapL(fn);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final  Choice3 biMapR(Fn1 fn) {
        return (Choice3) Bifunctor.super.biMapR(fn);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final  Choice3 biMap(Fn1 lFn,
                                               Fn1 rFn) {
        return match(Choice3::a, b -> b(lFn.apply(b)), c -> c(rFn.apply(c)));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public  Choice3 pure(D d) {
        return c(d);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public  Choice3 zip(Applicative, Choice3> appFn) {
        return MonadRec.super.zip(appFn).coerce();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public  Lazy> lazyZip(
            Lazy, Choice3>> lazyAppFn) {
        return match(a -> lazy(a(a)),
                     b -> lazy(b(b)),
                     c -> lazyAppFn.fmap(choiceF -> choiceF.fmap(f -> f.apply(c)).coerce()));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public  Choice3 discardL(Applicative> appB) {
        return MonadRec.super.discardL(appB).coerce();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public  Choice3 discardR(Applicative> appB) {
        return MonadRec.super.discardR(appB).coerce();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public  Choice3 flatMap(Fn1>> f) {
        return match(Choice3::a, Choice3::b, c -> f.apply(c).coerce());
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public  Choice3 trampolineM(
            Fn1, Choice3>> fn) {
        return flatMap(trampoline(c -> fn.apply(c).>>coerce()
                .match(a -> terminate(a(a)),
                       b -> terminate(b(b)),
                       r -> r.fmap(Choice3::c))));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public , TravB extends Traversable>,
            AppTrav extends Applicative> AppTrav traverse(Fn1> fn,
                                                                      Fn1 pure) {
        return match(a -> pure.apply(Choice3.a(a).coerce()),
                     b -> pure.apply(Choice3.b(b).coerce()),
                     c -> fn.apply(c).>fmap(Choice3::c).fmap(Functor::coerce))
                .coerce();
    }

    /**
     * Static factory method for wrapping a value of type A in a {@link Choice3}.
     *
     * @param a   the value
     * @param  the first possible type
     * @param  the second possible type
     * @param  the third possible type
     * @return the wrapped value as a {@link Choice3}<A, B, C>
     */
    public static  Choice3 a(A a) {
        return new _A<>(a);
    }

    /**
     * Static factory method for wrapping a value of type A in a {@link Choice3}.
     *
     * @param b   the value
     * @param  the first possible type
     * @param  the second possible type
     * @param  the third possible type
     * @return the wrapped value as a {@link Choice3}<A, B, C>
     */
    public static  Choice3 b(B b) {
        return new _B<>(b);
    }

    /**
     * Static factory method for wrapping a value of type A in a {@link Choice3}.
     *
     * @param c   the value
     * @param  the first possible type
     * @param  the second possible type
     * @param  the third possible type
     * @return the wrapped value as a {@link Choice3}<A, B, C>
     */
    public static  Choice3 c(C c) {
        return new _C<>(c);
    }

    /**
     * The canonical {@link Pure} instance for {@link Choice3}.
     *
     * @param  the first possible type
     * @param  the second possible type
     * @return the {@link Pure} instance
     */
    public static  Pure> pureChoice() {
        return Choice3::c;
    }

    private static final class _A extends Choice3 {

        private final A a;

        private _A(A a) {
            this.a = a;
        }

        @Override
        public  R match(Fn1 aFn, Fn1 bFn,
                           Fn1 cFn) {
            return aFn.apply(a);
        }

        @Override
        public boolean equals(Object other) {
            return other instanceof _A
                    && Objects.equals(a, ((_A) other).a);
        }

        @Override
        public int hashCode() {
            return Objects.hash(a);
        }

        @Override
        public String toString() {
            return "Choice3{" +
                    "a=" + a +
                    '}';
        }
    }

    private static final class _B extends Choice3 {

        private final B b;

        private _B(B b) {
            this.b = b;
        }

        @Override
        public  R match(Fn1 aFn, Fn1 bFn,
                           Fn1 cFn) {
            return bFn.apply(b);
        }

        @Override
        public boolean equals(Object other) {
            return other instanceof _B
                    && Objects.equals(b, ((_B) other).b);
        }

        @Override
        public int hashCode() {
            return Objects.hash(b);
        }

        @Override
        public String toString() {
            return "Choice3{" +
                    "b=" + b +
                    '}';
        }
    }

    private static final class _C extends Choice3 {

        private final C c;

        private _C(C c) {
            this.c = c;
        }

        @Override
        public  R match(Fn1 aFn, Fn1 bFn,
                           Fn1 cFn) {
            return cFn.apply(c);
        }

        @Override
        public boolean equals(Object other) {
            return other instanceof _C
                    && Objects.equals(c, ((_C) other).c);
        }

        @Override
        public int hashCode() {
            return Objects.hash(c);
        }

        @Override
        public String toString() {
            return "Choice3{" +
                    "c=" + c +
                    '}';
        }
    }
}