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

Stephen Weeks MLton@mlton.org
Wed, 19 Oct 2005 10:23:47 -0700


> 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,

Yeah, in the coding I've been doing with fold over the last month or
two, I've come to the conclusion that step0 and step1 are really all
that should be used, and mostly step0.  In cases where I've used stepN
for larger N, for example

  val s = Fold.step3 (fn (x1, x2, x3, z) => e)

     fold ... s x1 x2 x3 ...

I've found it leads to clearer code to go back to using step0, as in

  fun s x1 x2 x3 = Fold.step0 (fn z => e)

     fold ... (s x1 x2 x3) ...

or possibly even

  fun s (x1, x2, x3) = Fold.step0 (fn z => e)

     fold ... (s (x1, x2, x3)) ...

Even though both of these require more parens in client code, I find
the parens make them both easier to read and easier to edit.

> 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:
...
> Now, instead of writing
> 
>   Fold.stepN
>     (fn (s, p1, ..., pN) => ...)
> 
> you would write
...
>               ____ n times ____
>              /                 \
>   FoldV.step FoldV.T ... FoldV.T $
>     (fn s & p1 & ... & pN => ...)

Another possibility is to break through the fold abstraction a little,
and instead of defining f via stepN with

  fun f $ = Fold.stepN (fn (s, x1, ..., xN) => e) $

define f using currying with

  fun f z x1 ... xN = Fold.fold z (Fold.step0 (fn s => e))

> I prefer having a "closed" interface rather than an interface that
> has some arbitrary limit even if it incurs some syntactic overhed. 

Complete agreement here.

> When you have a closed interface, you can always introduce shorthand
> definitions outside the implementation.

If a closed interface has some useful shorthand, it is often
reasonable to expose the in the signature.  This makes it easier for
clients, and pushes toward some commonality in client code, which
makes for easier reading.