Import Upstream version 20180207
[hcoop/debian/mlton.git] / doc / guide / src / Printf.adoc
1 Printf
2 ======
3
4 Programmers coming from C or Java often ask if
5 <:StandardML:Standard ML> has a `printf` function. It does not.
6 However, it is possible to implement your own version with only a few
7 lines of code.
8
9 Here is a definition for `printf` and `fprintf`, along with format
10 specifiers for booleans, integers, and reals.
11
12 [source,sml]
13 ----
14 structure Printf =
15 struct
16 fun $ (_, f) = f (fn p => p ()) ignore
17 fun fprintf out f = f (out, id)
18 val printf = fn z => fprintf TextIO.stdOut z
19 fun one ((out, f), make) g =
20 g (out, fn r =>
21 f (fn p =>
22 make (fn s =>
23 r (fn () => (p (); TextIO.output (out, s))))))
24 fun ` x s = one (x, fn f => f s)
25 fun spec to x = one (x, fn f => f o to)
26 val B = fn z => spec Bool.toString z
27 val I = fn z => spec Int.toString z
28 val R = fn z => spec Real.toString z
29 end
30 ----
31
32 Here's an example use.
33
34 [source,sml]
35 ----
36 val () = printf `"Int="I`" Bool="B`" Real="R`"\n" $ 1 false 2.0
37 ----
38
39 This prints the following.
40
41 ----
42 Int=1 Bool=false Real=2.0
43 ----
44
45 In general, a use of `printf` looks like
46
47 ----
48 printf <spec1> ... <specn> $ <arg1> ... <argm>
49 ----
50
51 where each `<speci>` is either a specifier like `B`, `I`, or `R`, or
52 is an inline string, like ++&grave;"foo"++. A backtick (+&grave;+)
53 must precede each inline string. Each `<argi>` must be of the
54 appropriate type for the corresponding specifier.
55
56 SML `printf` is more powerful than its C counterpart in a number of
57 ways. In particular, the function produced by `printf` is a perfectly
58 ordinary SML function, and can be passed around, used multiple times,
59 etc. For example:
60
61 [source,sml]
62 ----
63 val f: int -> bool -> unit = printf `"Int="I`" Bool="B`"\n" $
64 val () = f 1 true
65 val () = f 2 false
66 ----
67
68 The definition of `printf` is even careful to not print anything until
69 it is fully applied. So, examples like the following will work as
70 expected.
71
72 ----
73 val f: int -> bool -> unit = printf `"Int="I`" Bool="B`"\n" $ 13
74 val () = f true
75 val () = f false
76 ----
77
78 It is also easy to define new format specifiers. For example, suppose
79 we wanted format specifiers for characters and strings.
80
81 ----
82 val C = fn z => spec Char.toString z
83 val S = fn z => spec (fn s => s) z
84 ----
85
86 One can define format specifiers for more complex types, e.g. pairs of
87 integers.
88
89 ----
90 val I2 =
91 fn z =>
92 spec (fn (i, j) =>
93 concat ["(", Int.toString i, ", ", Int.toString j, ")"])
94 z
95 ----
96
97 Here's an example use.
98
99 ----
100 val () = printf `"Test "I2`" a string "S`"\n" $ (1, 2) "hello"
101 ----
102
103
104 == Printf via <:Fold:> ==
105
106 `printf` is best viewed as a special case of variable-argument
107 <:Fold:> that inductively builds a function as it processes its
108 arguments. Here is the definition of a `Printf` structure in terms of
109 fold. The structure is equivalent to the above one, except that it
110 uses the standard `$` instead of a specialized one.
111
112 [source,sml]
113 ----
114 structure Printf =
115 struct
116 fun fprintf out =
117 Fold.fold ((out, id), fn (_, f) => f (fn p => p ()) ignore)
118
119 val printf = fn z => fprintf TextIO.stdOut z
120
121 fun one ((out, f), make) =
122 (out, fn r =>
123 f (fn p =>
124 make (fn s =>
125 r (fn () => (p (); TextIO.output (out, s))))))
126
127 val ` =
128 fn z => Fold.step1 (fn (s, x) => one (x, fn f => f s)) z
129
130 fun spec to = Fold.step0 (fn x => one (x, fn f => f o to))
131
132 val B = fn z => spec Bool.toString z
133 val I = fn z => spec Int.toString z
134 val R = fn z => spec Real.toString z
135 end
136 ----
137
138 Viewing `printf` as a fold opens up a number of possibilities. For
139 example, one can name parts of format strings using the fold idiom for
140 naming sequences of steps.
141
142 ----
143 val IB = fn u => Fold.fold u `"Int="I`" Bool="B
144 val () = printf IB`" "IB`"\n" $ 1 true 3 false
145 ----
146
147 One can even parametrize over partial format strings.
148
149 ----
150 fun XB X = fn u => Fold.fold u `"X="X`" Bool="B
151 val () = printf (XB I)`" "(XB R)`"\n" $ 1 true 2.0 false
152 ----
153
154
155 == Also see ==
156
157 * <:PrintfGentle:>
158 * <!Cite(Danvy98, Functional Unparsing)>