package haskell.prelude;

import haskell.lang.Data;

/**
 * data (a, b)
 */
public class Tuple<A, B>
        extends AbstractOrd<Tuple<A, B>>
        implements Data<Tuple<A, B>>,
                Eq<Tuple<A, B>>,
                Ord<Tuple<A, B>>,
                Bounded<Tuple<A, B>> {

    final A x;
    final B y;

    Tuple(final A x, final B y) {
        super("(,)");
        this.x = x;
        this.y = y;
    }

    public int compareTo(final Tuple<A, B> o) {
        // TODO Auto-generated method stub
        return 0;
    }

    // ---- data (a, b)

    public static <A, B> Tuple<A, B> Tuple_(final A x, final B y) {
        return new Tuple<A, B>(x, y);
    }

    /**
     * fst :: (a, b) -> a
     * fst (x, y) = x
     */
    public static <A, B> A fst(final Tuple<A, B> p) {
        return p.eval().x;
    }

    /**
     * snd :: (a, b) -> b
     * snd (x, y) = y
     */
    public static <A, B> B snd(final Tuple<A, B> p) {
        return p.eval().y;
    }

    /**
     * curry :: ((a, b) -> c) -> a -> b -> c
     * curry f x y = f (x, y)
     */
    public static <A, B, C> Function2<A, B, C> curry(
            final Function<Tuple<A, B>, C> f) {
        return new Function2<A, B, C>() {
            @Override
            public C apply(final A x, final B y) {
                return f.apply(Tuple_(x, y));
            }
        };
    }

    /**
     * uncurry :: (a -> b -> c) -> (a, b) -> c
     * uncurry f p = f (fst p) (snd p)
     */
    public static <A, B, C>
            Function<Tuple<A, B>, C> uncurry(final Function2<A, B, C> f) {
        return new Function<Tuple<A, B>, C>() {
            @Override
            public C apply(final Tuple<A, B> p) {
                return f.apply(fst(p), snd(p));
            }
        };
    }

    protected Eq.Support<Tuple<A, B>> _Eq_() {
        // TODO Auto-generated method stub
        return null;
    }

    protected Ord.Support<Tuple<A, B>> _Ord_() {
        // TODO Auto-generated method stub
        return null;
    }

}
