OptionalExpression.java

package lucidglitch;

import java.util.Optional;
import java.util.function.BiFunction;
import java.util.function.Function;

public class OptionalExpression {
    public static <T1> OptionalExpression.Step1<T1> of(Optional<T1> t) {
        return new OptionalExpression.Step1<>(t);
    }


    public static class Step1<T1> {
        private final Optional<T1> a;

        public Step1(Optional<T1> a) {
            this.a = a;
        }

        public <T2> OptionalExpression.Step2<T1, T2> bind(Function<T1, Optional<T2>> f) {
            return new Step2<>(a, f);
        }



        public <T2> Step2<T1, T2> map(Function<T1, T2> f) {
            return new Step2<>(a, t1 -> Optional.ofNullable(f.apply(t1)));
        }


        public <R> Optional<R> yield(Function<T1, R> f) {
            return a.map(t -> f.apply(t));
        }
    }

    public static class Step2<T1, T2> {
        private final Optional<T1> r;
        private final Function<T1, Optional<T2>> f;

        public Step2(Optional<T1> r,
                     Function<T1, Optional<T2>> f) {
            this.r = r;
            this.f = f;
        }

        public <T3> OptionalExpression.Step3<T1, T2, T3> bind(BiFunction<T1, T2, Optional<T3>> f1) {
            return new OptionalExpression.Step3<>(r,f, f1);
        }

        public <T3> Step3<T1, T2, T3> map(BiFunction<T1, T2, T3> f3) {
            return new Step3<>(r, f, (t1, t2) -> Optional.ofNullable(f3.apply(t1, t2)));
        }




        public <R> Optional<R> yield(BiFunction<T1,T2, R> yieldFn) {
            return r.flatMap(t1 ->
                    f.apply(t1).map(t2 -> yieldFn.apply(t1, t2)));
        }
    }

    public static class Step3<T1, T2, T3> {
        private final Optional<T1> r;
        private final Function<T1, Optional<T2>> f1;
        private final BiFunction<T1, T2, Optional<T3>> f2;

        public Step3(Optional<T1> r,
                     Function<T1, Optional<T2>> f1,
                     BiFunction<T1, T2, Optional<T3>> f2) {
            this.r = r;
            this.f1 = f1;
            this.f2 = f2;
        }

        public <T4> OptionalExpression.Step4<T1, T2, T3, T4> bind(Function3<T1, T2, T3, Optional<T4>> f3) {
            return new OptionalExpression.Step4<>(r, f1, f2, f3);
        }

        public <T4> Step4<T1, T2, T3, T4> map(Function3<T1, T2, T3, T4> f3) {
            return new Step4<>(r, f1, f2, (t1, t2, t3) -> Optional.ofNullable(f3.apply(t1, t2, t3)));
        }

        public <R> Optional<R> yield(Function3<T1, T2, T3, R> yieldFn) {
            return r.flatMap(t1 ->
                    f1.apply(t1).flatMap(t2 ->
                            f2.apply(t1, t2).map(t3 ->
                                    yieldFn.apply(t1, t2, t3)
                            )
                    )
            );
        }
    }
    public static class Step4<T1, T2, T3, T4> {
        private final Optional<T1> r;
        private final Function<T1, Optional<T2>> f1;
        private final BiFunction<T1, T2, Optional<T3>> f2;
        private final Function3<T1, T2, T3, Optional<T4>> f3;

        public Step4(Optional<T1> r,
                     Function<T1, Optional<T2>> f1,
                     BiFunction<T1, T2, Optional<T3>> f2,
                     Function3<T1, T2, T3, Optional<T4>> f3) {
            this.r = r;
            this.f1 = f1;
            this.f2 = f2;
            this.f3 = f3;
        }

        public <T5> OptionalExpression.Step5<T1, T2, T3, T4, T5> bind(Function4<T1, T2, T3, T4, Optional<T5>> f4) {
            return new OptionalExpression.Step5<>(r, f1, f2, f3, f4);
        }
        public <T5> Step5<T1, T2, T3, T4, T5> map(Function4<T1, T2, T3, T4, T5> f4) {
            return new Step5<>(r, f1, f2, f3, (t1, t2, t3, t4) -> Optional.ofNullable(f4.apply(t1, t2, t3, t4)));
        }
        public <R> Optional<R> yield(Function4<T1, T2, T3, T4, R> yieldFn) {
            return r.flatMap(t1 ->
                    f1.apply(t1).flatMap(t2 ->
                            f2.apply(t1, t2).flatMap(t3 ->
                                    f3.apply(t1, t2, t3).map(t4 ->
                                            yieldFn.apply(t1, t2, t3, t4)
                                    )
                            )
                    )
            );
        }
    }

    public static class Step5<T1, T2, T3, T4, T5> {
        private final Optional<T1> r;
        private final Function<T1, Optional<T2>> f1;
        private final BiFunction<T1, T2, Optional<T3>> f2;
        private final Function3<T1, T2, T3, Optional<T4>> f3;
        private final Function4<T1, T2, T3, T4, Optional<T5>> f4;

        public Step5(Optional<T1> r,
                     Function<T1, Optional<T2>> f1,
                     BiFunction<T1, T2, Optional<T3>> f2,
                     Function3<T1, T2, T3, Optional<T4>> f3,
                     Function4<T1, T2, T3, T4, Optional<T5>> f4) {
            this.r = r;
            this.f1 = f1;
            this.f2 = f2;
            this.f3 = f3;
            this.f4 = f4;
        }

        public <T6> OptionalExpression.Step6<T1, T2, T3, T4, T5, T6> bind(Function5<T1, T2, T3, T4, T5, Optional<T6>> f5) {
            return new OptionalExpression.Step6<>(r, f1, f2, f3, f4, f5);
        }
        public <T6> Step6<T1, T2, T3, T4, T5, T6> map(Function5<T1, T2, T3, T4, T5, T6> f5) {
            return new Step6<>(r, f1, f2, f3, f4, (t1, t2, t3, t4, t5) -> Optional.ofNullable(f5.apply(t1, t2, t3, t4, t5)));
        }
        public <R> Optional<R> yield(Function5<T1, T2, T3, T4, T5, R> yieldFn) {
            return r.flatMap(t1 ->
                    f1.apply(t1).flatMap(t2 ->
                            f2.apply(t1, t2).flatMap(t3 ->
                                    f3.apply(t1, t2, t3).flatMap(t4 ->
                                            f4.apply(t1, t2, t3, t4).map(t5 ->
                                                    yieldFn.apply(t1, t2, t3, t4, t5)
                                            )
                                    )
                            )
                    )
            );
        }
    }
        public static class Step6<T1, T2, T3, T4, T5, T6> {
            private final Optional<T1> r;
            private final Function<T1, Optional<T2>> f1;
            private final BiFunction<T1, T2, Optional<T3>> f2;
            private final Function3<T1, T2, T3, Optional<T4>> f3;
            private final Function4<T1, T2, T3, T4, Optional<T5>> f4;
            private final Function5<T1, T2, T3, T4, T5, Optional<T6>> f5;

            public Step6(Optional<T1> r,
                         Function<T1, Optional<T2>> f1,
                         BiFunction<T1, T2, Optional<T3>> f2,
                         Function3<T1, T2, T3, Optional<T4>> f3,
                         Function4<T1, T2, T3, T4, Optional<T5>> f4,
                         Function5<T1, T2, T3, T4, T5, Optional<T6>> f5) {

                this.r = r;
                this.f1 = f1;
                this.f2 = f2;
                this.f3 = f3;
                this.f4 = f4;
                this.f5 = f5;
            }

            public <T7> OptionalExpression.Step7<T1, T2, T3, T4, T5, T6, T7> bind(Function6<T1, T2, T3, T4, T5, T6, Optional<T7>> f6) {
                return new OptionalExpression.Step7<>(r, f1, f2, f3, f4, f5, f6);
            }

            public <T7> Step7<T1, T2, T3, T4, T5, T6, T7> map(Function6<T1, T2, T3, T4, T5, T6, T7> f6) {
                return new Step7<>(r, f1, f2, f3, f4, f5, (t1, t2, t3, t4, t5, t6) -> Optional.ofNullable(f6.apply(t1, t2, t3, t4, t5, t6)));
            }

            public <R> Optional<R> yield(Function6<T1, T2, T3, T4, T5, T6, R> yieldFn) {
                return r.flatMap(t1 ->
                        f1.apply(t1).flatMap(t2 ->
                                f2.apply(t1, t2).flatMap(t3 ->
                                        f3.apply(t1, t2, t3).flatMap(t4 ->
                                                f4.apply(t1, t2, t3, t4).flatMap(t5 ->
                                                        f5.apply(t1, t2, t3, t4, t5).map(t6 ->
                                                                yieldFn.apply(t1, t2, t3, t4, t5, t6)
                                                        )
                                                )
                                        )
                                )
                        )
                );
            }
        }



    public static class Step7<T1, T2, T3, T4, T5, T6, T7> {
        private final Optional<T1> r;
        private final Function<T1, Optional<T2>> f1;
        private final BiFunction<T1, T2, Optional<T3>> f2;
        private final Function3<T1, T2, T3, Optional<T4>> f3;
        private final Function4<T1, T2, T3, T4, Optional<T5>> f4;
        private final Function5<T1, T2, T3, T4, T5, Optional<T6>> f5;
        private final Function6<T1, T2, T3, T4, T5, T6, Optional<T7>> f6;

        public Step7(Optional<T1> r,
                     Function<T1, Optional<T2>> f1,
                     BiFunction<T1, T2, Optional<T3>> f2,
                     Function3<T1, T2, T3, Optional<T4>> f3,
                     Function4<T1, T2, T3, T4, Optional<T5>> f4,
                     Function5<T1, T2, T3, T4, T5, Optional<T6>> f5,
                     Function6<T1, T2, T3, T4, T5, T6, Optional<T7>> f6) {
            this.r = r;
            this.f1 = f1;
            this.f2 = f2;
            this.f3 = f3;
            this.f4 = f4;
            this.f5 = f5;
            this.f6 = f6;
        }

        public <T8> OptionalExpression.Step8<T1, T2, T3, T4, T5, T6, T7, T8> bind(Function7<T1, T2, T3, T4, T5, T6, T7, Optional<T8>> f7) {
            return new OptionalExpression.Step8<>(r, f1, f2, f3, f4, f5, f6, f7);
        }
        public <T8> Step8<T1, T2, T3, T4, T5, T6, T7, T8> map(Function7<T1, T2, T3, T4, T5, T6, T7, T8> f7) {
            return new Step8<>(r, f1, f2, f3, f4, f5, f6, (t1, t2, t3, t4, t5, t6, t7) -> Optional.ofNullable(f7.apply(t1, t2, t3, t4, t5, t6, t7)));
        }
        public <R> Optional<R> yield(Function7<T1, T2, T3, T4, T5, T6, T7, R> yieldFn) {
            return r.flatMap(t1 ->
                    f1.apply(t1).flatMap(t2 ->
                            f2.apply(t1, t2).flatMap(t3 ->
                                    f3.apply(t1, t2, t3).flatMap(t4 ->
                                            f4.apply(t1, t2, t3, t4).flatMap(t5 ->
                                                    f5.apply(t1, t2, t3, t4, t5).flatMap(t6 ->
                                                            f6.apply(t1, t2, t3, t4, t5, t6).map(t7 ->
                                                                    yieldFn.apply(t1, t2, t3, t4, t5, t6, t7)
                                                            )
                                                    )
                                            )
                                    )
                            )
                    )
            );
        }
    }

    public static class Step8<T1, T2, T3, T4, T5, T6, T7, T8> {
        private final Optional<T1> r;
        private final Function<T1, Optional<T2>> f1;
        private final BiFunction<T1, T2, Optional<T3>> f2;
        private final Function3<T1, T2, T3, Optional<T4>> f3;
        private final Function4<T1, T2, T3, T4, Optional<T5>> f4;
        private final Function5<T1, T2, T3, T4, T5, Optional<T6>> f5;
        private final Function6<T1, T2, T3, T4, T5, T6, Optional<T7>> f6;
        private final Function7<T1, T2, T3, T4, T5, T6, T7, Optional<T8>> f7;

        public Step8(Optional<T1> r,
                     Function<T1, Optional<T2>> f1,
                     BiFunction<T1, T2, Optional<T3>> f2,
                     Function3<T1, T2, T3, Optional<T4>> f3,
                     Function4<T1, T2, T3, T4, Optional<T5>> f4,
                     Function5<T1, T2, T3, T4, T5, Optional<T6>> f5,
                     Function6<T1, T2, T3, T4, T5, T6, Optional<T7>> f6,
                     Function7<T1, T2, T3, T4, T5, T6, T7, Optional<T8>> f7) {
            this.r = r;
            this.f1 = f1;
            this.f2 = f2;
            this.f3 = f3;
            this.f4 = f4;
            this.f5 = f5;
            this.f6 = f6;
            this.f7 = f7;
        }

        public <T9> OptionalExpression.Step9<T1, T2, T3, T4, T5, T6, T7, T8, T9> bind(Function8<T1, T2, T3, T4, T5, T6, T7, T8, Optional<T9>> f8) {
            return new OptionalExpression.Step9<>(r, f1, f2, f3, f4, f5, f6, f7, f8);
        }

        public <T9> Step9<T1, T2, T3, T4, T5, T6, T7, T8, T9> map(Function8<T1, T2, T3, T4, T5, T6, T7, T8, T9> f8) {
            return new Step9<>(r, f1, f2, f3, f4, f5, f6, f7, (t1, t2, t3, t4, t5, t6, t7, t8) -> Optional.ofNullable(f8.apply(t1, t2, t3, t4, t5, t6, t7, t8)));
        }
        public <R> Optional<R> yield(Function8<T1, T2, T3, T4, T5, T6, T7, T8, R> yieldFn) {
            return r.flatMap(t1 ->
                    f1.apply(t1).flatMap(t2 ->
                            f2.apply(t1, t2).flatMap(t3 ->
                                    f3.apply(t1, t2, t3).flatMap(t4 ->
                                            f4.apply(t1, t2, t3, t4).flatMap(t5 ->
                                                    f5.apply(t1, t2, t3, t4, t5).flatMap(t6 ->
                                                            f6.apply(t1, t2, t3, t4, t5, t6).flatMap(t7 ->
                                                                    f7.apply(t1, t2, t3, t4, t5, t6, t7).map(t8 ->
                                                                            yieldFn.apply(t1, t2, t3, t4, t5, t6, t7, t8)
                                                                    )
                                                            )
                                                    )
                                            )
                                    )
                            )
                    )
            );
        }
    }

    public static class Step9<T1, T2, T3, T4, T5, T6, T7, T8, T9> {
        private final Optional<T1> r;
        private final Function<T1, Optional<T2>> f1;
        private final BiFunction<T1, T2, Optional<T3>> f2;
        private final Function3<T1, T2, T3, Optional<T4>> f3;
        private final Function4<T1, T2, T3, T4, Optional<T5>> f4;
        private final Function5<T1, T2, T3, T4, T5, Optional<T6>> f5;
        private final Function6<T1, T2, T3, T4, T5, T6, Optional<T7>> f6;
        private final Function7<T1, T2, T3, T4, T5, T6, T7, Optional<T8>> f7;
        private final Function8<T1, T2, T3, T4, T5, T6, T7, T8, Optional<T9>> f8;

        public Step9(Optional<T1> r,
                     Function<T1, Optional<T2>> f1,
                     BiFunction<T1, T2, Optional<T3>> f2,
                     Function3<T1, T2, T3, Optional<T4>> f3,
                     Function4<T1, T2, T3, T4, Optional<T5>> f4,
                     Function5<T1, T2, T3, T4, T5, Optional<T6>> f5,
                     Function6<T1, T2, T3, T4, T5, T6, Optional<T7>> f6,
                     Function7<T1, T2, T3, T4, T5, T6, T7, Optional<T8>> f7,
                     Function8<T1, T2, T3, T4, T5, T6, T7, T8, Optional<T9>> f8) {
            this.r = r;
            this.f1 = f1;
            this.f2 = f2;
            this.f3 = f3;
            this.f4 = f4;
            this.f5 = f5;
            this.f6 = f6;
            this.f7 = f7;
            this.f8 = f8;
        }

        public <R> Optional<R> yield(Function9<T1, T2, T3, T4, T5, T6, T7, T8, T9, R> yieldFn) {
            return r.flatMap(t1 ->
                    f1.apply(t1).flatMap(t2 ->
                            f2.apply(t1, t2).flatMap(t3 ->
                                    f3.apply(t1, t2, t3).flatMap(t4 ->
                                            f4.apply(t1, t2, t3, t4).flatMap(t5 ->
                                                    f5.apply(t1, t2, t3, t4, t5).flatMap(t6 ->
                                                            f6.apply(t1, t2, t3, t4, t5, t6).flatMap(t7 ->
                                                                    f7.apply(t1, t2, t3, t4, t5, t6, t7).flatMap(t8 ->
                                                                            f8.apply(t1, t2, t3, t4, t5, t6, t7, t8).map(t9 ->
                                                                                    yieldFn.apply(t1, t2, t3, t4, t5, t6, t7, t8, t9)
                                                                            )
                                                                    )
                                                            )
                                                    )
                                            )
                                    )
                            )
                    )
            );
        }
    }
    @FunctionalInterface
    public interface Function3<A1, A2, A3, R> {
        R apply(A1 a1, A2 a2, A3 a3);
    }


    @FunctionalInterface
    public interface Function4<A1, A2, A3, A4, R> {
        R apply(A1 a1, A2 a2, A3 a3, A4 a4);
    }


    @FunctionalInterface
    public interface Function5<A1, A2, A3, A4, A5, R> {
        R apply(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5);
    }


    @FunctionalInterface
    public interface Function6<A1, A2, A3, A4, A5, A6, R> {
        R apply(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6);
    }


    @FunctionalInterface
    public interface Function7<A1, A2, A3, A4, A5, A6, A7, R> {
        R apply(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7);
    }


    @FunctionalInterface
    public interface Function8<A1, A2, A3, A4, A5, A6, A7, A8, R> {
        R apply(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8);
    }


    @FunctionalInterface
    public interface Function9<A1, A2, A3, A4, A5, A6, A7, A8, A9, R> {
        R apply(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9);
    }
}