Import Upstream version 20180207
[hcoop/debian/mlton.git] / doc / guide / src / ArrayLiteral.adoc
CommitLineData
7f918cf1
CE
1ArrayLiteral
2============
3
4<:StandardML:Standard ML> does not have a syntax for array literals or
5vector literals. The only way to write down an array is like
6[source,sml]
7----
8Array.fromList [w, x, y, z]
9----
10
11No SML compiler produces efficient code for the above expression. The
12generated code allocates a list and then converts it to an array. To
13alleviate this, one could write down the same array using
14`Array.tabulate`, or even using `Array.array` and `Array.update`, but
15that is syntactically unwieldy.
16
17Fortunately, using <:Fold:>, it is possible to define constants `A`,
18and +&grave;+ so that one can write down an array like:
19[source,sml]
20----
21A `w `x `y `z $
22----
23This is as syntactically concise as the `fromList` expression.
24Furthermore, MLton, at least, will generate the efficient code as if
25one had written down a use of `Array.array` followed by four uses of
26`Array.update`.
27
28Along with `A` and +&grave;+, one can define a constant `V` that makes
29it possible to define vector literals with the same syntax, e.g.,
30[source,sml]
31----
32V `w `x `y `z $
33----
34
35Note that the same element indicator, +&grave;+, serves for both array
36and vector literals. Of course, the `$` is the end-of-arguments
37marker always used with <:Fold:>. The only difference between an
38array literal and vector literal is the `A` or `V` at the beginning.
39
40Here is the implementation of `A`, `V`, and +&grave;+. We place them
41in a structure and use signature abstraction to hide the type of the
42accumulator. See <:Fold:> for more on this technique.
43[source,sml]
44----
45structure Literal:>
46 sig
47 type 'a z
48 val A: ('a z, 'a z, 'a array, 'd) Fold.t
49 val V: ('a z, 'a z, 'a vector, 'd) Fold.t
50 val ` : ('a, 'a z, 'a z, 'b, 'c, 'd) Fold.step1
51 end =
52 struct
53 type 'a z = int * 'a option * ('a array -> unit)
54
55 val A =
56 fn z =>
57 Fold.fold
58 ((0, NONE, ignore),
59 fn (n, opt, fill) =>
60 case opt of
61 NONE =>
62 Array.tabulate (0, fn _ => raise Fail "array0")
63 | SOME x =>
64 let
65 val a = Array.array (n, x)
66 val () = fill a
67 in
68 a
69 end)
70 z
71
72 val V = fn z => Fold.post (A, Array.vector) z
73
74 val ` =
75 fn z =>
76 Fold.step1
77 (fn (x, (i, opt, fill)) =>
78 (i + 1,
79 SOME x,
80 fn a => (Array.update (a, i, x); fill a)))
81 z
82 end
83----
84
85The idea of the code is for the fold to accumulate a count of the
86number of elements, a sample element, and a function that fills in all
87the elements. When the fold is complete, the finishing function
88allocates the array, applies the fill function, and returns the array.
89The only difference between `A` and `V` is at the very end; `A` just
90returns the array, while `V` converts it to a vector using
91post-composition, which is further described on the <:Fold:> page.