package example.javaee;

import haskell.lang.AbstractExp;
import haskell.prelude.Function;
import haskell.prelude.Function2;
import haskell.prelude.IO;
import haskell.prelude.Int;

public class FunctionExample {

    /**
     * f1 :: Int -> Int
     * f1 x = x - 2
     */
    public static Int f1(final Int x) {
        return x._minus_(Int.valueOf(2));
    }

    static final Function<Int, Int> f1 = new Function<Int, Int>(
            FunctionExample.class, Int.class, "f1", Int.class);
            // [関数名,] 定義クラス, 戻り値の型, メソッド名, [引数の型...]

    /**
     * f2 :: Int -> Int
     * f2 x = x + 3
     */
    static final Function<Int, Int> f2
            = new Function<Int, Int>("f2") {
        @Override
        public Int apply(final Int x) {
            return new Int(new AbstractExp<Int>(toString(x)) {
                public Int eval() {
                    return x._plus_(Int.valueOf(3));
                }
            });
        }
    };

    /**
     * f3 :: Int -> Int
     * f3 = f1 . f2
     */
    static final Function<Int, Int> f3 = f1._dot_(f2);

    /**
     * f4 :: Int -> Int -> Int
     * f4 x y = f1 x * f2 y
     */
    static final Function2<Int, Int, Int> f4
            = new Function2<Int, Int, Int>("f4") {
        @Override
        public Int apply(final Int x, final Int y) {
            return new Int(new AbstractExp<Int>(toString(x, y)) {
                public Int eval() {
                    return f1.apply(x)._multiply_(f2.apply(y));
                }
            });
        }
    };

    /**
     * f5 :: Int -> Int
     * f5 = f4 5
     */
    static final Function<Int, Int> f5 = f4.apply(Int.valueOf(5));

    public static void main(String[] args) {
        IO.putStr("f1 1 => ");
        IO.print(f1.apply(Int.valueOf(1)));
        IO.putStr("f2 2 => ");
        IO.print(f2.apply(Int.valueOf(2)));
        IO.putStr("f3 3 => ");
        IO.print(f3.apply(Int.valueOf(3)));
        IO.putStr("f4 4 5 => ");
        IO.print(f4.apply(Int.valueOf(4), Int.valueOf(5)));
        IO.putStr("f5 6 => ");
        IO.print(f5.apply(Int.valueOf(6)));
    }

}
