Commit | Line | Data |
---|---|---|
7f918cf1 CE |
1 | ArrayLiteral |
2 | ============ | |
3 | ||
4 | <:StandardML:Standard ML> does not have a syntax for array literals or | |
5 | vector literals. The only way to write down an array is like | |
6 | [source,sml] | |
7 | ---- | |
8 | Array.fromList [w, x, y, z] | |
9 | ---- | |
10 | ||
11 | No SML compiler produces efficient code for the above expression. The | |
12 | generated code allocates a list and then converts it to an array. To | |
13 | alleviate this, one could write down the same array using | |
14 | `Array.tabulate`, or even using `Array.array` and `Array.update`, but | |
15 | that is syntactically unwieldy. | |
16 | ||
17 | Fortunately, using <:Fold:>, it is possible to define constants `A`, | |
18 | and +`+ so that one can write down an array like: | |
19 | [source,sml] | |
20 | ---- | |
21 | A `w `x `y `z $ | |
22 | ---- | |
23 | This is as syntactically concise as the `fromList` expression. | |
24 | Furthermore, MLton, at least, will generate the efficient code as if | |
25 | one had written down a use of `Array.array` followed by four uses of | |
26 | `Array.update`. | |
27 | ||
28 | Along with `A` and +`+, one can define a constant `V` that makes | |
29 | it possible to define vector literals with the same syntax, e.g., | |
30 | [source,sml] | |
31 | ---- | |
32 | V `w `x `y `z $ | |
33 | ---- | |
34 | ||
35 | Note that the same element indicator, +`+, serves for both array | |
36 | and vector literals. Of course, the `$` is the end-of-arguments | |
37 | marker always used with <:Fold:>. The only difference between an | |
38 | array literal and vector literal is the `A` or `V` at the beginning. | |
39 | ||
40 | Here is the implementation of `A`, `V`, and +`+. We place them | |
41 | in a structure and use signature abstraction to hide the type of the | |
42 | accumulator. See <:Fold:> for more on this technique. | |
43 | [source,sml] | |
44 | ---- | |
45 | structure 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 | ||
85 | The idea of the code is for the fold to accumulate a count of the | |
86 | number of elements, a sample element, and a function that fills in all | |
87 | the elements. When the fold is complete, the finishing function | |
88 | allocates the array, applies the fill function, and returns the array. | |
89 | The only difference between `A` and `V` is at the very end; `A` just | |
90 | returns the array, while `V` converts it to a vector using | |
91 | post-composition, which is further described on the <:Fold:> page. |