[MLton] Fold01N

Stephen Weeks MLton@mlton.org
Tue, 18 Oct 2005 17:08:36 -0700


There is an idiom that we have now seen several times when using
varargs fold with products, where one wants to treat the cases of zero
or one arguments specially.  One typically has a combinator "g" and
finisher "z" in mind and wants to define a folder "f" and stepper "`"
such that the following evaluations hold.

   f $            -->  z ()
   f`x1 $         -->  z x1
   f`x1 ...`xn $  -->  z (g (... (g (g (x1, x2), x3) ...), xn))

Here is a module, Fold01N, that expresses the idiom, defined using
Fold.

----------------------------------------------------------------------
datatype ('a, 'b) product = & of 'a * 'b
infix 4 &
fun $ (a, f) = f a
fun curry h x y = h (x, y)
fun id x = x
fun ignore _ = ()
fun pass x f = f x

structure Fold =
   struct
      type ('a, 'b, 'c, 'd) step = 'a * ('b -> 'c) -> 'd
      type ('a, 'b, 'c, 'd) t = ('a, 'b, 'c, 'd) step -> 'd
      type ('a1, 'a2, 'b, 'c, 'd) step0 =
         ('a1, 'b, 'c, ('a2, 'b, 'c, 'd) t) step
      type ('a1, 'a2, 'a3, 'b, 'c, 'd) step1 =
         ('a2, 'b, 'c, 'a1 -> ('a3, 'b, 'c, 'd) t) step

      val fold = pass
      fun step0 h (a1, f) = fold (h a1, f)
      fun step1 h $ x = step0 (curry h x) $
   end

structure Fold01N =
   struct
      type ('a, 'b, 'c, 'd, 'e) t =
         ((unit -> unit) * ('c -> 'c), (unit -> 'a) * 'd, 'b, 'e) Fold.t
      type ('a, 'b, 'c, 'z1, 'z2, 'z3, 'z4, 'z5) step1 =
         ('z1,
          'z2 * ('z1 -> 'a),
          (unit -> 'a) * ('b -> 'c),
          'z3, 'z4, 'z5) Fold.step1
          
      val fold: ('a -> 'b) -> ('a, 'b, 'c, 'd, 'e) t =
         fn finish =>
         Fold.fold ((ignore, id), fn (p, _) => finish (p ()))
         
      val step1
         : ('a * 'b -> 'c) -> ('a, 'b, 'c, 'z1, 'z2, 'z3, 'z4, 'z5) step1 =
         fn combine =>
         Fold.step1 (fn (x, (_, f)) =>
                     (fn () => f x, fn x' => combine (f x, x')))
   end
----------------------------------------------------------------------

As a simple example, one can define an f (and `) that creates the
product of all its arguments.

----------------------------------------------------------------------
val f = fn $ => Fold01N.fold id $
val ` = fn $ => Fold01N.step1 (op &) $
val () = f $
val 13 = f`13 $
val 1 & 2 & 3 = f`1`2`3 $
----------------------------------------------------------------------

Perhaps Fold01N should be generalized slightly to allow more varied
behavior in the zero and one cases.