/*
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 * @copyright Ranides Atterwim
 * @license WTFPL
 * @url http://ranides.net/projects/assira
 */
package net.ranides.assira.functional.covariant;

import java.io.Serializable;
import java.util.function.Function;

/**
 * Strictly speaking, it is not functional interface, because it requires to provide two,
 * complementary operations, where the second is inversion of the first.
 *
 * Every projection should meet conditions for every "x" and "z":
 *      invert( apply(x) ) := x
 *      apply( invert(z) ) := z
 *
 * If you want to create projection in place, by writing only lambda expressions, you can use
 * static "produce" methods, which create composite object from provided lambda expressions.
 *
 * @param <R> R
 * @param <T> T
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 */
public interface ProjectionFunction<T,R> extends Function<T,R>, CoFunction<T,R> {

    /**
     * Returns identity projection
     *
     * @param <R> R
     * @return functional
     */
    static <R> ProjectionFunction<R,R> identity() {
        return produce(v->v, v->v);
    }

    /**
     * Creates composite projection with provided implementations
     *
     * @param f function
     * @param cf inverted function
     * @param <T> T
     * @param <R> R
     * @return functional
     */
    static <T,R> ProjectionFunction<T,R> produce(Function<T,R> f, CoFunction<T,R> cf) {
        class PF implements ProjectionFunction<T,R>, Serializable {
			
			private static final long serialVersionUID = 1L;
			
            @Override
            public R apply(T value) {
                return f.apply(value);
            }

            @Override
            public T invert(R value) {
                return cf.invert(value);
            }
        }
        return new PF();
    }
    
}
