Import Upstream version 20180207
[hcoop/debian/mlton.git] / doc / guide / src / InfixingOperators.adoc
CommitLineData
7f918cf1
CE
1InfixingOperators
2=================
3
4Fixity specifications are not part of signatures in
5<:StandardML:Standard ML>. When one wants to use a module that
6provides functions designed to be used as infix operators there are
7several obvious alternatives:
8
9* Use only prefix applications. Unfortunately there are situations
10where infix applications lead to considerably more readable code.
11
12* Make the fixity declarations at the top-level. This may lead to
13collisions and may be unsustainable in a large project. Pollution of
14the top-level should be avoided.
15
16* Make the fixity declarations at each scope where you want to use
17infix applications. The duplication becomes inconvenient if the
18operators are widely used. Duplication of code should be avoided.
19
20* Use non-standard extensions, such as the <:MLBasis: ML Basis system>
21to control the scope of fixity declarations. This has the obvious
22drawback of reduced portability.
23
24* Reuse existing infix operator symbols (`^`, `+`, `-`, ...). This
25can be convenient when the standard operators aren't needed in the
26same scope with the new operators. On the other hand, one is limited
27to the standard operator symbols and the code may appear confusing.
28
29None of the obvious alternatives is best in every case. The following
30describes a slightly less obvious alternative that can sometimes be
31useful. The idea is to approximate Haskell's special syntax for
32treating any identifier enclosed in grave accents (backquotes) as an
33infix operator. In Haskell, instead of writing the prefix application
34`f x y` one can write the infix application ++x &grave;f&grave; y++.
35
36
37== Infixing operators ==
38
39Let's first take a look at the definitions of the operators:
40
41[source,sml]
42----
43infix 3 <\ fun x <\ f = fn y => f (x, y) (* Left section *)
44infix 3 \> fun f \> y = f y (* Left application *)
45infixr 3 /> fun f /> y = fn x => f (x, y) (* Right section *)
46infixr 3 </ fun x </ f = f x (* Right application *)
47
48infix 2 o (* See motivation below *)
49infix 0 :=
50----
51
52The left and right sectioning operators, `<\` and `/>`, are useful in
53SML for partial application of infix operators.
54<!Cite(Paulson96, ML For the Working Programmer)> describes curried
55functions `secl` and `secr` for the same purpose on pages 179-181.
56For example,
57
58[source,sml]
59----
60List.map (op- /> y)
61----
62
63is a function for subtracting `y` from a list of integers and
64
65[source,sml]
66----
67List.exists (x <\ op=)
68----
69
70is a function for testing whether a list contains an `x`.
71
72Together with the left and right application operators, `\>` and `</`,
73the sectioning operators provide a way to treat any binary function
74(i.e. a function whose domain is a pair) as an infix operator. In
75general,
76
77----
78x0 <\f1\> x1 <\f2\> x2 ... <\fN\> xN = fN (... f2 (f1 (x0, x1), x2) ..., xN)
79----
80
81and
82
83----
84xN </fN/> ... x2 </f2/> x1 </f1/> x0 = fN (xN, ... f2 (x2, f1 (x1, x0)) ...)
85----
86
87
88=== Examples ===
89
90As a fairly realistic example, consider providing a function for sequencing
91comparisons:
92
93[source,sml]
94----
95structure Order (* ... *) =
96 struct
97 (* ... *)
98 val orWhenEq = fn (EQUAL, th) => th ()
99 | (other, _) => other
100 (* ... *)
101 end
102----
103Using `orWhenEq` and the infixing operators, one can write a
104`compare` function for triples as
105
106[source,sml]
107----
108fun compare (fad, fbe, fcf) ((a, b, c), (d, e, f)) =
109 fad (a, d) <\Order.orWhenEq\> `fbe (b, e) <\Order.orWhenEq\> `fcf (c, f)
110----
111
112where +&grave;+ is defined as
113
114[source,sml]
115----
116fun `f x = fn () => f x
117----
118
119Although `orWhenEq` can be convenient (try rewriting the above without
120it), it is probably not useful enough to be defined at the top level
121as an infix operator. Fortunately we can use the infixing operators
122and don't have to.
123
124Another fairly realistic example would be to use the infixing operators with
125the technique described on the <:Printf:> page. Assuming that you would have
126a `Printf` module binding `printf`, +&grave;+, and formatting combinators
127named `int` and `string`, you could write
128
129[source,sml]
130----
131let open Printf in
132 printf (`"Here's an int "<\int\>" and a string "<\string\>".") 13 "foo" end
133----
134
135without having to duplicate the fixity declarations. Alternatively, you could
136write
137
138[source,sml]
139----
140P.printf (P.`"Here's an int "<\P.int\>" and a string "<\P.string\>".") 13 "foo"
141----
142
143assuming you have the made the binding
144
145[source,sml]
146----
147structure P = Printf
148----
149
150
151== Application and piping operators ==
152
153The left and right application operators may also provide some notational
154convenience on their own. In general,
155
156----
157f \> x1 \> ... \> xN = f x1 ... xN
158----
159
160and
161
162----
163xN </ ... </ x1 </ f = f x1 ... xN
164----
165
166If nothing else, both of them can eliminate parentheses. For example,
167
168[source,sml]
169----
170foo (1 + 2) = foo \> 1 + 2
171----
172
173The left and right application operators are related to operators
174that could be described as the right and left piping operators:
175
176[source,sml]
177----
178infix 1 >| val op>| = op</ (* Left pipe *)
179infixr 1 |< val op|< = op\> (* Right pipe *)
180----
181
182As you can see, the left and right piping operators, `>|` and `|<`,
183are the same as the right and left application operators,
184respectively, except the associativities are reversed and the binding
185strength is lower. They are useful for piping data through a sequence
186of operations. In general,
187
188----
189x >| f1 >| ... >| fN = fN (... (f1 x) ...) = (fN o ... o f1) x
190----
191
192and
193
194----
195fN |< ... |< f1 |< x = fN (... (f1 x) ...) = (fN o ... o f1) x
196----
197
198The right piping operator, `|<`, is provided by the Haskell prelude as
199`$`. It can be convenient in CPS or continuation passing style.
200
201A use for the left piping operator is with parsing combinators. In a
202strict language, like SML, eta-reduction is generally unsafe. Using
203the left piping operator, parsing functions can be formatted
204conveniently as
205
206[source,sml]
207----
208fun parsingFunc input =
209 input >| (* ... *)
210 || (* ... *)
211 || (* ... *)
212----
213
214where `||` is supposed to be a combinator provided by the parsing combinator
215library.
216
217
218== About precedences ==
219
220You probably noticed that we redefined the
221<:OperatorPrecedence:precedences> of the function composition operator
222`o` and the assignment operator `:=`. Doing so is not strictly
223necessary, but can be convenient and should be relatively
224safe. Consider the following motivating examples from
225<:WesleyTerpstra: Wesley W. Terpstra> relying on the redefined
226precedences:
227
228[source,sml]
229----
230Word8.fromInt o Char.ord o s <\String.sub
231(* Combining sectioning and composition *)
232
233x := s <\String.sub\> i
234(* Assigning the result of an infixed application *)
235----
236
237In imperative languages, assignment usually has the lowest precedence
238(ignoring statement separators). The precedence of `:=` in the
239<:BasisLibrary: Basis Library> is perhaps unnecessarily high, because
240an expression of the form `r := x` always returns a unit, which makes
241little sense to combine with anything. Dropping `:=` to the lowest
242precedence level makes it behave more like in other imperative
243languages.
244
245The case for `o` is different. With the exception of `before` and
246`:=`, it doesn't seem to make much sense to use `o` with any of the
247operators defined by the <:BasisLibrary: Basis Library> in an
248unparenthesized expression. This is simply because none of the other
249operators deal with functions. It would seem that the precedence of
250`o` could be chosen completely arbitrarily from the set `{1, ..., 9}`
251without having any adverse effects with respect to other infix
252operators defined by the <:BasisLibrary: Basis Library>.
253
254
255== Design of the symbols ==
256
257The closest approximation of Haskell's ++x &grave;f&grave; y++ syntax
258achievable in Standard ML would probably be something like
259++x &grave;f^ y++, but `^` is already used for string
260concatenation by the <:BasisLibrary: Basis Library>. Other
261combinations of the characters +&grave;+ and `^` would be
262possible, but none seems clearly the best visually. The symbols `<\`,
263`\>`, `</`, and `/>` are reasonably concise and have a certain
264self-documenting appearance and symmetry, which can help to remember
265them. As the names suggest, the symbols of the piping operators `>|`
266and `|<` are inspired by Unix shell pipelines.
267
268
269== Also see ==
270
271 * <:Utilities:>