## MLton 20241230

# ArrayLiteral

Standard ML does not have a syntax for array literals or vector literals. The only way to write down an array is like

    Array.fromList [w, x, y, z]

No SML compiler produces efficient code for the above expression. The generated code allocates a list and then converts it to an array. To alleviate this, one could write down the same array using `Array.tabulate`, or even using `Array.array` and `Array.update`, but that is syntactically unwieldy.

Fortunately, using Fold, it is possible to define constants `A`, and ``` so that one can write down an array like:

    A `w `x `y `z $

This is as syntactically concise as the `fromList` expression. Furthermore, MLton, at least, will generate the efficient code as if one had written down a use of `Array.array` followed by four uses of `Array.update`.

Along with `A` and ```, one can define a constant `V` that makes it possible to define vector literals with the same syntax, e.g.,

    V `w `x `y `z $

Note that the same element indicator, ```, serves for both array and vector literals. Of course, the `$` is the end-of-arguments marker always used with Fold. The only difference between an array literal and vector literal is the `A` or `V` at the beginning.

Here is the implementation of `A`, `V`, and ```. We place them in a structure and use signature abstraction to hide the type of the accumulator. See Fold for more on this technique.

    structure Literal:>
       sig
          type 'a z
          val A: ('a z, 'a z, 'a array, 'd) Fold.t
          val V: ('a z, 'a z, 'a vector, 'd) Fold.t
          val ` : ('a, 'a z, 'a z, 'b, 'c, 'd) Fold.step1
       end =
       struct
          type 'a z = int * 'a option * ('a array -> unit)

          val A =
             fn z =>
             Fold.fold
             ((0, NONE, ignore),
              fn (n, opt, fill) =>
              case opt of
                 NONE =>
                    Array.tabulate (0, fn _ => raise Fail "array0")
               | SOME x =>
                    let
                       val a = Array.array (n, x)
                       val () = fill a
                    in
                       a
                    end)
             z

          val V = fn z => Fold.post (A, Array.vector) z

          val ` =
             fn z =>
             Fold.step1
             (fn (x, (i, opt, fill)) =>
              (i + 1,
               SOME x,
               fn a => (Array.update (a, i, x); fill a)))
             z
       end

The idea of the code is for the fold to accumulate a count of the number of elements, a sample element, and a function that fills in all the elements. When the fold is complete, the finishing function allocates the array, applies the fill function, and returns the array. The only difference between `A` and `V` is at the very end; `A` just returns the array, while `V` converts it to a vector using post-composition, which is further described on the Fold page.