/** 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.lazy(__ -> f.eval().call(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);
  }

  /** 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 Thunk.ready(f ->
    Thunk.ready(b -> Thunk.ready(a ->
      Fn.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> call(final Thunk<Arg> arg);
}

@@ -634,20 +634,21 @@
 .eval().
-call
+apply
 (x).eval
@@ -2425,20 +2425,21 @@
 nk%3CRet%3E 
-call
+apply
 (final T

rename Fn.{call => apply} for consistency and correctness