[MLton] Fold: stepN == step T ... T $

Vesa Karvonen vesa.karvonen@cs.helsinki.fi
Sun, 16 Oct 2005 20:15:50 +0300


The original Fold formulation contains functions of types of the form

  val step0 : ... ('a -> 'b) ...
  val step1 : ... ('a * 'b -> 'c) ...
  val step2 : ... ('a * 'b * 'c -> 'd) ...
  ...

Although you'll probably never need a huge number of such functions, it
would nevertheless be nice to avoid such indexed functions. As usual, we
can get a fairly convenient alternative with a constant number of
functions (two in this case) using infix products and CPS:

  datatype ('a, 'b) product = & of 'a * 'b
  infix &
  fun id x = x
  fun pass x f = f x
  fun $ (x, f) = f x

  structure FoldC =
     struct
        val fold = pass
        fun step h (s, f) = fold (h s, f)
        fun take k (s, f) x = fold (s & x, f) k
     end

Now, instead of writing

  Fold.stepN
    (fn (s, p1, ..., pN) => ...)

you would write

   _________ n times _________
  /                           \
 (FoldC.take o ... o FoldC.take o FoldC.step)
    (fn s & p1 & ... & pN => ...)

We can also apply the technique to itself to reduce the syntactic
overhead:

  structure FoldV =
     struct
        val fold = FoldC.fold
        fun step $ = FoldC.fold (FoldC.step, id) $
        fun T $ = FoldC.step (fn step => FoldC.take o step) $
     end

Now you would write

              ____ n times ____
             /                 \
  FoldV.step FoldV.T ... FoldV.T $
    (fn s & p1 & ... & pN => ...)

I think that the optional arguments formulation could also use a similar
reform. I prefer having a "closed" interface rather than an interface that
has some arbitrary limit even if it incurs some syntactic overhed. When
you have a closed interface, you can always introduce shorthand definitions
outside the implementation.

-Vesa Karvonen