/**
 * Created by edlc on 12/9/16.
 * @memberOf module:functor
 */
import Functor from './Functor';

/**
 * Bifunctor class;  Mostly useful for eithers and/or maybes.
 * @class module:functor.Bifunctor
 * @param value1 {*}
 * @param value2 {*}
 * @property value {*}
 * @property value2 {*}
 * @extends module:functor.Functor
 */
export default class Bifunctor extends Functor {

    /**
     * @param value1 {*}
     * @param value2 {*}
     * @private
     * @returns {Bifunctor}
     */
    constructor(value1, value2) {
        super(value1);
        this.value2 = value2;
    }

    /**
     * Returns wrapped 'second' value.
     * @method module:functor.Bifunctor#value2Of
     * @returns {*}
     */
    value2Of() {
        return this.value2;
    }

    /**
     * Allows you to map over first 'contained' value.
     * @method module:functor.Bifunctor#first
     * @param fn {Function} - Unary operation.
     * @returns {Bifunctor}
     */
    first (fn) {
        return new this.constructor(fn(this.valueOf()), this.value2Of());
    }

    /**
     * Allows you to map over second 'contained' value.
     * @method module:functor.Bifunctor#second
     * @param fn {Function} - Unary operation.
     * @returns {Bifunctor}
     */
    second (fn) {
        return new this.constructor(this.valueOf(), fn(this.value2Of()));
    }

    /**
     * Allows you to map 2 functions over contained values - One function over each value.
     * @method module:functor.Bifunctor#bimap
     * @param fn1 {Function} - Unary op.
     * @param fn2 {Function} - Unary op.
     * @returns {Bifunctor}
     */
    bimap (fn1, fn2) {
        return new this.constructor(
            fn1(this.valueOf()),
            fn2(this.value2Of())
        );
    }
}