Import Upstream version 20180207
[hcoop/debian/mlton.git] / doc / guide / src / OptionalArguments.adoc
CommitLineData
7f918cf1
CE
1OptionalArguments
2=================
3
4<:StandardML:Standard ML> does not have built-in support for optional
5arguments. Nevertheless, using <:Fold:>, it is easy to define
6functions that take optional arguments.
7
8For example, suppose that we have the following definition of a
9function `f`.
10
11[source,sml]
12----
13fun f (i, r, s) =
14 concat [Int.toString i, ", ", Real.toString r, ", ", s]
15----
16
17Using the `OptionalArg` structure described below, we can define a
18function `f'`, an optionalized version of `f`, that takes 0, 1, 2, or
193 arguments. Embedded within `f'` will be default values for `i`,
20`r`, and `s`. If `f'` gets no arguments, then all the defaults are
21used. If `f'` gets one argument, then that will be used for `i`. Two
22arguments will be used for `i` and `r` respectively. Three arguments
23will override all default values. Calls to `f'` will look like the
24following.
25
26[source,sml]
27----
28f' $
29f' `2 $
30f' `2 `3.0 $
31f' `2 `3.0 `"four" $
32----
33
34The optional argument indicator, +&grave;+, is not special syntax ---
35it is a normal SML value, defined in the `OptionalArg` structure
36below.
37
38Here is the definition of `f'` using the `OptionalArg` structure, in
39particular, `OptionalArg.make` and `OptionalArg.D`.
40
41[source,sml]
42----
43val f' =
44 fn z =>
45 let open OptionalArg in
46 make (D 1) (D 2.0) (D "three") $
47 end (fn i & r & s => f (i, r, s))
48 z
49----
50
51The definition of `f'` is eta expanded as with all uses of fold. A
52call to `OptionalArg.make` is supplied with a variable number of
53defaults (in this case, three), the end-of-arguments terminator, `$`,
54and the function to run, taking its arguments as an n-ary
55<:ProductType:product>. In this case, the function simply converts
56the product to an ordinary tuple and calls `f`. Often, the function
57body will simply be written directly.
58
59In general, the definition of an optional-argument function looks like
60the following.
61
62[source,sml]
63----
64val f =
65 fn z =>
66 let open OptionalArg in
67 make (D <default1>) (D <default2>) ... (D <defaultn>) $
68 end (fn x1 & x2 & ... & xn =>
69 <function code goes here>)
70 z
71----
72
73Here is the definition of `OptionalArg`.
74
75[source,sml]
76----
77structure OptionalArg =
78 struct
79 val make =
80 fn z =>
81 Fold.fold
82 ((id, fn (f, x) => f x),
83 fn (d, r) => fn func =>
84 Fold.fold ((id, d ()), fn (f, d) =>
85 let
86 val d & () = r (id, f d)
87 in
88 func d
89 end))
90 z
91
92 fun D d = Fold.step0 (fn (f, r) =>
93 (fn ds => f (d & ds),
94 fn (f, a & b) => r (fn x => f a & x, b)))
95
96 val ` =
97 fn z =>
98 Fold.step1 (fn (x, (f, _ & d)) => (fn d => f (x & d), d))
99 z
100 end
101----
102
103`OptionalArg.make` uses a nested fold. The first `fold` accumulates
104the default values in a product, associated to the right, and a
105reversal function that converts a product (of the same arity as the
106number of defaults) from right associativity to left associativity.
107The accumulated defaults are used by the second fold, which recurs
108over the product, replacing the appropriate component as it encounters
109optional arguments. The second fold also constructs a "fill"
110function, `f`, that is used to reconstruct the product once the
111end-of-arguments is reached. Finally, the finisher reconstructs the
112product and uses the reversal function to convert the product from
113right associative to left associative, at which point it is passed to
114the user-supplied function.
115
116Much of the complexity comes from the fact that while recurring over a
117product from left to right, one wants it to be right-associative,
118e.g., look like
119
120[source,sml]
121----
122a & (b & (c & d))
123----
124
125but the user function in the end wants the product to be left
126associative, so that the product argument pattern can be written
127without parentheses (since `&` is left associative).
128
129
130== Labelled optional arguments ==
131
132In addition to the positional optional arguments described above, it
133is sometimes useful to have labelled optional arguments. These allow
134one to define a function, `f`, with defaults, say `a` and `b`. Then,
135a caller of `f` can supply values for `a` and `b` by name. If no
136value is supplied then the default is used.
137
138Labelled optional arguments are a simple extension of
139<:FunctionalRecordUpdate:> using post composition. Suppose, for
140example, that one wants a function `f` with labelled optional
141arguments `a` and `b` with default values `0` and `0.0` respectively.
142If one has a functional-record-update function `updateAB` for records
143with `a` and `b` fields, then one can define `f` in the following way.
144
145[source,sml]
146----
147val f =
148 fn z =>
149 Fold.post
150 (updateAB {a = 0, b = 0.0},
151 fn {a, b} => print (concat [Int.toString a, " ",
152 Real.toString b, "\n"]))
153 z
154----
155
156The idea is that `f` is the post composition (using `Fold.post`) of
157the actual code for the function with a functional-record updater that
158starts with the defaults.
159
160Here are some example calls to `f`.
161[source,sml]
162----
163val () = f $
164val () = f (U#a 13) $
165val () = f (U#a 13) (U#b 17.5) $
166val () = f (U#b 17.5) (U#a 13) $
167----
168
169Notice that a caller can supply neither of the arguments, either of
170the arguments, or both of the arguments, and in either order. All
171that matter is that the arguments be labelled correctly (and of the
172right type, of course).
173
174Here is another example.
175
176[source,sml]
177----
178val f =
179 fn z =>
180 Fold.post
181 (updateBCD {b = 0, c = 0.0, d = "<>"},
182 fn {b, c, d} =>
183 print (concat [Int.toString b, " ",
184 Real.toString c, " ",
185 d, "\n"]))
186 z
187----
188
189Here are some example calls.
190
191[source,sml]
192----
193val () = f $
194val () = f (U#d "goodbye") $
195val () = f (U#d "hello") (U#b 17) (U#c 19.3) $
196----