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

com.jnape.palatable.lambda.adt.coproduct.CoProduct4 Maven / Gradle / Ivy

package com.jnape.palatable.lambda.adt.coproduct;

import com.jnape.palatable.lambda.adt.Maybe;
import com.jnape.palatable.lambda.adt.hlist.Tuple4;
import com.jnape.palatable.lambda.functions.Fn1;

import java.util.function.Function;

import static com.jnape.palatable.lambda.adt.Maybe.just;
import static com.jnape.palatable.lambda.adt.Maybe.nothing;
import static com.jnape.palatable.lambda.adt.hlist.HList.tuple;
import static com.jnape.palatable.lambda.functions.Fn1.fn1;
import static com.jnape.palatable.lambda.functions.builtin.fn1.Constantly.constantly;

/**
 * A generalization of the coproduct of four types.
 *
 * @param  the first possible type
 * @param  the second possible type
 * @param  the third possible type
 * @param  the fourth possible type
 * @see CoProduct2
 */
@FunctionalInterface
public interface CoProduct4> {

    /**
     * Type-safe convergence requiring a match against all potential types.
     *
     * @param aFn morphism A -> R
     * @param bFn morphism B -> R
     * @param cFn morphism C -> R
     * @param dFn morphism D -> R
     * @param  result type
     * @return the result of applying the appropriate morphism from whichever type is represented by this coproduct to R
     * @see CoProduct2#match(Function, Function)
     */
     R match(Function aFn,
                Function bFn,
                Function cFn,
                Function dFn);

    /**
     * Diverge this coproduct by introducing another possible type that it could represent.
     *
     * @param  the additional possible type of this coproduct
     * @return a Coproduct5<A, B, C, D, E>
     * @see CoProduct2#diverge()
     */
    default  CoProduct5> diverge() {
        return new CoProduct5>() {
            @Override
            public  R match(Function aFn, Function bFn,
                               Function cFn, Function dFn,
                               Function eFn) {
                return CoProduct4.this.match(aFn, bFn, cFn, dFn);
            }
        };
    }

    /**
     * Converge this coproduct down to a lower order coproduct by mapping the last possible type into an earlier
     * possible type.
     *
     * @param convergenceFn function from last possible type to earlier type
     * @return a {@link CoProduct3}<A, B, C>
     * @see CoProduct3#converge
     */
    default CoProduct3> converge(
            Function> convergenceFn) {
        return match(a -> new CoProduct3>() {
            @Override
            public  R match(Function aFn, Function bFn,
                               Function cFn) {
                return aFn.apply(a);
            }
        }, b -> new CoProduct3>() {
            @Override
            public  R match(Function aFn, Function bFn,
                               Function cFn) {
                return bFn.apply(b);
            }
        }, c -> new CoProduct3>() {
            @Override
            public  R match(Function aFn, Function bFn,
                               Function cFn) {
                return cFn.apply(c);
            }
        }, convergenceFn);
    }

    /**
     * Project this coproduct onto a tuple.
     *
     * @return a tuple of the coproduct projection
     * @see CoProduct2#project()
     */
    default Tuple4, Maybe, Maybe, Maybe> project() {
        return match(a -> tuple(just(a), nothing(), nothing(), nothing()),
                     b -> tuple(nothing(), just(b), nothing(), nothing()),
                     c -> tuple(nothing(), nothing(), just(c), nothing()),
                     d -> tuple(nothing(), nothing(), nothing(), just(d)));
    }

    /**
     * Convenience method for projecting this coproduct onto a tuple and then extracting the first slot value.
     *
     * @return an optional value representing the projection of the "a" type index
     */
    default Maybe projectA() {
        return project()._1();
    }

    /**
     * Convenience method for projecting this coproduct onto a tuple and then extracting the second slot value.
     *
     * @return an optional value representing the projection of the "b" type index
     */
    default Maybe projectB() {
        return project()._2();
    }

    /**
     * Convenience method for projecting this coproduct onto a tuple and then extracting the third slot value.
     *
     * @return an optional value representing the projection of the "c" type index
     */
    default Maybe projectC() {
        return project()._3();
    }

    /**
     * Convenience method for projecting this coproduct onto a tuple and then extracting the fourth slot value.
     *
     * @return an optional value representing the projection of the "d" type index
     */
    default Maybe projectD() {
        return project()._4();
    }

    /**
     * Embed this coproduct inside another value; that is, given morphisms from this coproduct to R, apply
     * the appropriate morphism to this coproduct as a whole. Like {@link CoProduct4#match}, but without unwrapping the
     * value.
     *
     * @param aFn morphism A v B v C v D -> R, applied in the A case
     * @param bFn morphism A v B v C v D -> R, applied in the B case
     * @param cFn morphism A v B v C v D -> R, applied in the C case
     * @param dFn morphism A v B v C v D -> R, applied in the D case
     * @param  result type
     * @return the result of applying the appropriate morphism to this coproduct
     */
    @SuppressWarnings("unchecked")
    default  R embed(Function aFn,
                        Function bFn,
                        Function cFn,
                        Function dFn) {
        return this.>match(constantly(fn1(aFn)),
                                       constantly(fn1(bFn)),
                                       constantly(fn1(cFn)),
                                       constantly(fn1(dFn)))
                .apply((CP4) this);
    }

}