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

com.jnape.palatable.lambda.functor.builtin.Compose Maven / Gradle / Ivy

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

import com.jnape.palatable.lambda.functions.Fn1;
import com.jnape.palatable.lambda.functions.specialized.Pure;
import com.jnape.palatable.lambda.functor.Applicative;

import java.util.Objects;

import static com.jnape.palatable.lambda.functions.builtin.fn1.Upcast.upcast;

/**
 * A functor representing the type-level composition of two {@link Applicative} functors; useful for preserving nested
 * type-level transformations during traversal of a {@link com.jnape.palatable.lambda.traversable.Traversable}.
 *
 * @param  The outer applicative
 * @param  The inner applicative
 * @param  The carrier type
 */
public final class Compose, G extends Applicative, A> implements
        Applicative> {

    private final Applicative, F> fga;

    public Compose(Applicative, F> fga) {
        this.fga = fga;
    }

    @SuppressWarnings("RedundantTypeArguments")
    public , FGA extends Applicative> FGA getCompose() {
        return fga.fmap(Applicative::coerce).coerce();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public  Compose fmap(Fn1 fn) {
        return new Compose<>(fga.fmap(g -> g.fmap(fn)));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public  Compose pure(B b) {
        return new Compose<>(fga.fmap(g -> g.pure(b)));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public  Compose zip(Applicative, Compose> appFn) {
        return new Compose<>(fga.zip(appFn.>>coerce()
                                             .getCompose().fmap(gFn -> g -> g.zip(gFn))));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public  Lazy> lazyZip(
            Lazy, Compose>> lazyAppFn) {
        @SuppressWarnings("RedundantTypeArguments")
        Lazy, G>, F>> lazyAppFnCoerced =
                lazyAppFn
                        .>>fmap(
                                Applicative, Compose>::coerce)
                        .fmap(Compose>::getCompose);

        return fga.>fmap(upcast())
                .>lazyZip(lazyAppFnCoerced.fmap(fgf -> fgf.fmap(gf -> ga -> ga.zip(gf))))
                .fmap(Compose::new);
    }

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

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

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

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

    @Override
    public String toString() {
        return "Compose{fga=" + fga + '}';
    }

    /**
     * The canonical {@link Pure} instance for {@link Compose}.
     *
     * @param pureF the {@link Pure} constructor for the outer {@link Applicative}
     * @param pureG the {@link Pure} constructor for the inner {@link Applicative}
     * @param    the outer {@link Applicative} type
     * @param    the inner {@link Applicative} type
     * @return the {@link Pure} instance
     */
    public static , G extends Applicative> Pure> pureCompose(
            Pure pureF, Pure pureG) {
        return new Pure>() {
            @Override
            public  Compose checkedApply(A a) throws Throwable {
                return new Compose<>(pureF., Applicative, F>>apply(pureG.apply(a)));
            }
        };
    }
}