package prelude;

import static prelude.Thunk.*;

/** A thunked computation from values of type Arg1 to values of type Ret.
 *
 * Not considering potential optimizations and hacks, this is the only place
 * in the codebase where direct method calls are allowed. Everything else must
 * use this as the gateway to evaluating the thunked graph.
 *
 * Another exception is code that wraps non-Haskell-style code: there, it is
 * allowed to use Java primitives and method calls, as well as invoke Thunk#eval().
 */
public interface Fn<Arg, Ret> {
  public static <Arg, Ret>
    Thunk<Ret> apply(Thunk<Fn<Arg, Ret>> f,
                      Thunk<Arg> x)
  {
    return thunk(__ -> f.eval().apply(x).eval());
  }

  /** convenience helper for applying a curried function to 2 arguments */
  public static <Arg1, Arg2, Ret>
    Thunk<Ret> apply2(Thunk<Fn<Arg1, Fn<Arg2, Ret>>> f,
                      Thunk<Arg1> a,
                      Thunk<Arg2> b)
  {
    return apply(apply(f, a), b);
  }

  public static <Arg1, Arg2, Ret>
    Thunk<Ret> infix(Thunk<Arg1> a,
                     Thunk<Fn<Arg1, Fn<Arg2, Ret>>> op,
                     Thunk<Arg2> b)
  {
    return apply2(op, a, b);
  }

  /** convenience helper for applying a curried function to 3 arguments */
  public static <Arg1, Arg2, Arg3, Ret>
    Thunk<Ret> apply3(Thunk<Fn<Arg1, Fn<Arg2, Fn<Arg3, Ret>>>> f,
                      Thunk<Arg1> a,
                      Thunk<Arg2> b,
                      Thunk<Arg3> c)
  {
    return apply(apply(apply(f, a), b), c);
  }

  /** convenience helper for applying a curried function to 3 arguments */
  public static <Arg1, Arg2, Arg3, Arg4, Ret>
    Thunk<Ret> apply3(Thunk<Fn<Arg1, Fn<Arg2, Fn<Arg3, Fn<Arg4, Ret>>>>> f,
                      Thunk<Arg1> a,
                      Thunk<Arg2> b,
                      Thunk<Arg3> c,
                      Thunk<Arg4> d)
  {
    return apply(apply(apply(apply(f, a), b), c), d);
  }

  /** Takes a 2-argument function and returnn a new function with the order of
    * these arguments flipped.
    *
    * In Haskell, the type of the function is:
    *
    *     flip :: (a -> b -> c) -> (b -> a -> c)
    */
  public static <Arg1, Arg2, Ret>
    Thunk<Fn<Fn<Arg1, Fn<Arg2, Ret>>,
             Fn<Arg2, Fn<Arg1, Ret>>>>
  flip() { return fn(f -> fn(b -> fn(a ->
      apply2(f, a, b)
  ))); }

  // this really ought to be private, but interface members are forcibly public,
  // but we can't use a class here because functional interfaces only work
  // with, well, interfaces, and we really need the lambda syntax support for
  // readability and type inference.
  Thunk<Ret> apply(final Thunk<Arg> arg);
}

@@ -559,26 +559,24 @@
  %3CArg, Ret%3E%0A
-  
   Thunk%3CRet%3E
@@ -571,24 +571,26 @@
   Thunk%3CRet%3E
+%0A 
  apply(Thunk
@@ -603,38 +603,24 @@
 g, Ret%3E%3E f,%0A
-              
         Thun
@@ -789,34 +789,32 @@
 rg1, Arg2, Ret%3E%0A
-  
   Thunk%3CRet%3E app
@@ -805,24 +805,26 @@
   Thunk%3CRet%3E
+%0A 
  apply2(Thun
@@ -849,37 +849,24 @@
 , Ret%3E%3E%3E f,%0A
-             
          Thu
@@ -873,37 +873,24 @@
 nk%3CArg1%3E a,%0A
-             
          Thu
@@ -970,34 +970,32 @@
 rg1, Arg2, Ret%3E%0A
-  
   Thunk%3CRet%3E inf
@@ -990,16 +990,18 @@
 unk%3CRet%3E
+%0A 
  infix(T
@@ -1018,37 +1018,24 @@
  a,%0A        
-      
-       
 Thunk%3CFn%3CArg
@@ -1057,29 +1057,16 @@
 %3E%3E%3E op,%0A
-             
         
@@ -1229,26 +1229,24 @@
  Arg3, Ret%3E%0A
-  
   Thunk%3CRet%3E
@@ -1237,32 +1237,34 @@
 et%3E%0A  Thunk%3CRet%3E
+%0A 
  apply3(Thunk%3CFn
@@ -1295,37 +1295,24 @@
  Ret%3E%3E%3E%3E f,%0A
-             
          Thu
@@ -1319,37 +1319,24 @@
 nk%3CArg1%3E a,%0A
-             
          Thu
@@ -1343,37 +1343,24 @@
 nk%3CArg2%3E b,%0A
-             
          Thu
@@ -1541,26 +1541,24 @@
  Arg4, Ret%3E%0A
-  
   Thunk%3CRet%3E
@@ -1553,24 +1553,26 @@
   Thunk%3CRet%3E
+%0A 
  apply3(Thun
@@ -1621,29 +1621,16 @@
 %3E%3E%3E%3E f,%0A
-             
         
@@ -1645,29 +1645,16 @@
 rg1%3E a,%0A
-             
         
@@ -1674,37 +1674,24 @@
 b,%0A         
-       
-      
 Thunk%3CArg3%3E 
@@ -1693,29 +1693,16 @@
 rg3%3E c,%0A
-             
         
@@ -2039,26 +2039,24 @@
 2, Ret%3E%0A
-  
   Thunk%3C
 Fn%3CFn%3CAr
@@ -2047,16 +2047,18 @@
   Thunk%3C
+  
 Fn%3CFn%3CAr
@@ -2114,16 +2114,18 @@
 , Ret%3E%3E%3E
+  
 %3E%0A  flip

reformat Thunk.java