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

com.jnape.palatable.lambda.functor.builtin.Writer 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.adt.Unit;
import com.jnape.palatable.lambda.adt.hlist.Tuple2;
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.monad.Monad;
import com.jnape.palatable.lambda.monad.MonadRec;
import com.jnape.palatable.lambda.monad.MonadWriter;
import com.jnape.palatable.lambda.monad.transformer.builtin.WriterT;
import com.jnape.palatable.lambda.monoid.Monoid;

import static com.jnape.palatable.lambda.adt.Unit.UNIT;
import static com.jnape.palatable.lambda.adt.hlist.HList.tuple;
import static com.jnape.palatable.lambda.functions.builtin.fn1.Constantly.constantly;
import static com.jnape.palatable.lambda.functions.builtin.fn2.Both.both;
import static com.jnape.palatable.lambda.functions.builtin.fn2.Into.into;
import static com.jnape.palatable.lambda.functions.recursion.Trampoline.trampoline;

/**
 * The lazy writer monad, a monad capturing some accumulation (eventually to be folded in terms of a given monoid) and
 * a value. Note that unlike the {@link State} monad, the {@link Writer} monad does not allow the value to be fully
 * derived from the accumulation.
 *
 * @param  the accumulation type
 * @param  the value type
 */
public final class Writer implements
        MonadWriter>,
        MonadRec> {

    private final Fn1, ? extends Tuple2> writerFn;

    private Writer(Fn1, ? extends Tuple2> writerFn) {
        this.writerFn = writerFn;
    }

    /**
     * Given a {@link Monoid} for the accumulation, run the computation represented by this {@link Writer}, accumulate
     * the written output in terms of the {@link Monoid}, and produce the accumulation and the value.
     *
     * @param monoid the accumulation {@link Monoid}
     * @return the accumulation with the value
     */
    public Tuple2 runWriter(Monoid monoid) {
        return writerFn.apply(monoid);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public  Writer> listens(Fn1 fn) {
        return new Writer<>(monoid -> runWriter(monoid).into((a, w) -> tuple(both(constantly(a), fn, w), w)));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Writer censor(Fn1 fn) {
        return new Writer<>(monoid -> runWriter(monoid).fmap(fn));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public  Writer trampolineM(Fn1, Writer>> fn) {
        return new Writer<>(monoid -> trampoline(into((a, w) -> fn.apply(a).>>coerce()
                .runWriter(monoid)
                .fmap(monoid.apply(w))
                .into((aOrB, w_) -> aOrB.biMap(a_ -> tuple(a_, w_), b -> tuple(b, w_)))), runWriter(monoid)));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public  Writer flatMap(Fn1>> f) {
        return new Writer<>(monoid -> writerFn.apply(monoid)
                .into((a, w) -> f.apply(a).>coerce().runWriter(monoid).fmap(monoid.apply(w))));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public  Writer pure(B b) {
        return listen(b);
    }

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

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

    /**
     * {@inheritDoc}
     */
    @Override
    public  Lazy> lazyZip(
            Lazy, Writer>> lazyAppFn) {
        return MonadRec.super.lazyZip(lazyAppFn).fmap(MonadRec>::coerce);
    }

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

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

    /**
     * Construct a {@link Writer} from an accumulation.
     *
     * @param w   the accumulation
     * @param  the accumulation type
     * @return the {@link Writer}
     */
    public static  Writer tell(W w) {
        return writer(tuple(UNIT, w));
    }

    /**
     * Construct a {@link Writer} from a value.
     *
     * @param a   the output value
     * @param  the accumulation type
     * @param  the value type
     * @return the {@link Writer}
     */
    public static  Writer listen(A a) {
        return Writer.pureWriter().apply(a);
    }

    /**
     * Construct a {@link Writer} from an accumulation and a value.
     *
     * @param aw  the output value and accumulation
     * @param  the accumulation type
     * @param  the value type
     * @return the {@link WriterT}
     */
    public static  Writer writer(Tuple2 aw) {
        return new Writer<>(constantly(aw));
    }

    /**
     * The canonical {@link Pure} instance for {@link Writer}.
     *
     * @param  the accumulation type
     * @return the {@link Pure} instance
     */
    public static  Pure> pureWriter() {
        return new Pure>() {
            @Override
            public  Writer checkedApply(A a) {
                return new Writer<>(monoid -> tuple(a, monoid.identity()));
            }
        };
    }
}