[MLton] MLton.Vector.create

Stephen Weeks MLton@mlton.org
Tue, 28 Mar 2006 09:04:29 -0800


There is a weakness in the basis library with regard to directly
creating vectors.  Vector.tabulate is useful in some situations, but
often one needs something more flexible.  In the basis, if
Vector.tabulate isn't sufficient, one must resort to creating an array
and then calling Array.vector, which copies the entire array.  This is
obviously wasteful, and can be especially problematic if the
array/vector is huge, in which case copying might not even be possible
in RAM.

Henry and I were talking yesterday about a possible alternative
interface, and came up with the following, which I propose to add to
the MLton.Vector structure.

  val create:
    int * 'a * ({sub: int -> 'a, update: int * 'a -> unit} -> unit) 
    -> 'a vector

The idea of create (n, x, f) is to create an array of size n,
initially filled with x for every element.  Then, pass "sub" and
"update" functions to f, which can do as much computation and mutation
of the array in any manner that it likes.  Then, when f is finished,
we, in constant time convert the array to a vector, using our (in
general unsafe) array->vector conversion function.  Once create has
returned the vector, calls to update will raise an exception.

Here's a proposed implementation of create.  The code lives in
basis-library/arrays-and-vectors/vector.sml.

      fun create (n, x, f) =
         let
            val a = Primitive.Array.array n
            val () =
               Util.naturalForeach (n, fn i => Primitive.Array.update (a, i, x))
            fun sub i =
               if Primitive.safe andalso Primitive.Int.geu (i, n) then
                  raise Subscript
               else
                  Primitive.Array.sub (a, i)
            val isFrozen = ref false
            val max = ref n
            fun update (i, x) =
               if Primitive.safe andalso Primitive.Int.geu (i, !max) then
                  if !isFrozen then
                     raise Fail "attempt to mutate frozen vector"
                  else
                     raise Subscript
               else
                  Primitive.Array.update (a, i, x)
            val () = f {sub = sub, update = update}
            val () = max := 0
            val () = isFrozen := true
         in
            fromArray a
         end