1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
|
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="generator" content="AsciiDoc 8.6.8">
<title>Printf</title>
<link rel="stylesheet" href="./asciidoc.css" type="text/css">
<link rel="stylesheet" href="./pygments.css" type="text/css">
<script type="text/javascript" src="./asciidoc.js"></script>
<script type="text/javascript">
/*<![CDATA[*/
asciidoc.install();
/*]]>*/
</script>
<link rel="stylesheet" href="./mlton.css" type="text/css"/>
</head>
<body class="article">
<div id="banner">
<div id="banner-home">
<a href="./Home">MLton 20130715</a>
</div>
</div>
<div id="header">
<h1>Printf</h1>
</div>
<div id="content">
<div id="preamble">
<div class="sectionbody">
<div class="paragraph"><p>Programmers coming from C or Java often ask if
<a href="StandardML">Standard ML</a> has a <span class="monospaced">printf</span> function. It does not.
However, it is possible to implement your own version with only a few
lines of code.</p></div>
<div class="paragraph"><p>Here is a definition for <span class="monospaced">printf</span> and <span class="monospaced">fprintf</span>, along with format
specifiers for booleans, integers, and reals.</p></div>
<div class="listingblock">
<div class="content"><div class="highlight"><pre><span class="k">structure</span><span class="w"> </span><span class="n">Printf</span><span class="w"> </span><span class="p">=</span><span class="w"></span>
<span class="w"> </span><span class="k">struct</span><span class="w"></span>
<span class="w"> </span><span class="k">fun</span><span class="w"> </span><span class="n">$</span><span class="w"> </span><span class="p">(_,</span><span class="w"> </span><span class="n">f</span><span class="p">)</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="p">(</span><span class="k">fn</span><span class="w"> </span><span class="n">p</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="n">p</span><span class="w"> </span><span class="p">())</span><span class="w"> </span><span class="n">ignore</span><span class="w"></span>
<span class="w"> </span><span class="k">fun</span><span class="w"> </span><span class="n">fprintf</span><span class="w"> </span><span class="n">out</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="p">(</span><span class="n">out</span><span class="p">,</span><span class="w"> </span><span class="n">id</span><span class="p">)</span><span class="w"></span>
<span class="w"> </span><span class="k">val</span><span class="w"> </span><span class="n">printf</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="k">fn</span><span class="w"> </span><span class="n">z</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="n">fprintf</span><span class="w"> </span><span class="n">TextIO</span><span class="p">.</span><span class="n">stdOut</span><span class="w"> </span><span class="n">z</span><span class="w"></span>
<span class="w"> </span><span class="k">fun</span><span class="w"> </span><span class="n">one</span><span class="w"> </span><span class="p">((</span><span class="n">out</span><span class="p">,</span><span class="w"> </span><span class="n">f</span><span class="p">),</span><span class="w"> </span><span class="n">make</span><span class="p">)</span><span class="w"> </span><span class="n">g</span><span class="w"> </span><span class="p">=</span><span class="w"></span>
<span class="w"> </span><span class="n">g</span><span class="w"> </span><span class="p">(</span><span class="n">out</span><span class="p">,</span><span class="w"> </span><span class="k">fn</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="p">=></span><span class="w"></span>
<span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="p">(</span><span class="k">fn</span><span class="w"> </span><span class="n">p</span><span class="w"> </span><span class="p">=></span><span class="w"></span>
<span class="w"> </span><span class="n">make</span><span class="w"> </span><span class="p">(</span><span class="k">fn</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="p">=></span><span class="w"></span>
<span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="p">(</span><span class="k">fn</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">(</span><span class="n">p</span><span class="w"> </span><span class="p">();</span><span class="w"> </span><span class="n">TextIO</span><span class="p">.</span><span class="n">output</span><span class="w"> </span><span class="p">(</span><span class="n">out</span><span class="p">,</span><span class="w"> </span><span class="n">s</span><span class="p">))))))</span><span class="w"></span>
<span class="w"> </span><span class="k">fun</span><span class="w"> </span><span class="n">`</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="n">one</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="k">fn</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">s</span><span class="p">)</span><span class="w"></span>
<span class="w"> </span><span class="k">fun</span><span class="w"> </span><span class="n">spec</span><span class="w"> </span><span class="n">to</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="n">one</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="k">fn</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">o</span><span class="w"> </span><span class="n">to</span><span class="p">)</span><span class="w"></span>
<span class="w"> </span><span class="k">val</span><span class="w"> </span><span class="n">B</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="k">fn</span><span class="w"> </span><span class="n">z</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="n">spec</span><span class="w"> </span><span class="n">Bool</span><span class="p">.</span><span class="n">toString</span><span class="w"> </span><span class="n">z</span><span class="w"></span>
<span class="w"> </span><span class="k">val</span><span class="w"> </span><span class="n">I</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="k">fn</span><span class="w"> </span><span class="n">z</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="n">spec</span><span class="w"> </span><span class="n">Int</span><span class="p">.</span><span class="n">toString</span><span class="w"> </span><span class="n">z</span><span class="w"></span>
<span class="w"> </span><span class="k">val</span><span class="w"> </span><span class="n">R</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="k">fn</span><span class="w"> </span><span class="n">z</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="n">spec</span><span class="w"> </span><span class="n">Real</span><span class="p">.</span><span class="n">toString</span><span class="w"> </span><span class="n">z</span><span class="w"></span>
<span class="w"> </span><span class="k">end</span><span class="w"></span>
</pre></div></div></div>
<div class="paragraph"><p>Here’s an example use.</p></div>
<div class="listingblock">
<div class="content"><div class="highlight"><pre><span class="k">val</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="n">printf</span><span class="w"> </span><span class="n">`</span><span class="s">"Int="</span><span class="n">I`</span><span class="s">" Bool="</span><span class="n">B`</span><span class="s">" Real="</span><span class="n">R`</span><span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="w"> </span><span class="n">$</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="n">false</span><span class="w"> </span><span class="mf">2.0</span><span class="w"></span>
</pre></div></div></div>
<div class="paragraph"><p>This prints the following.</p></div>
<div class="listingblock">
<div class="content monospaced">
<pre>Int=1 Bool=false Real=2.0</pre>
</div></div>
<div class="paragraph"><p>In general, a use of <span class="monospaced">printf</span> looks like</p></div>
<div class="listingblock">
<div class="content monospaced">
<pre>printf <spec1> ... <specn> $ <arg1> ... <argm></pre>
</div></div>
<div class="paragraph"><p>where each <span class="monospaced"><speci></span> is either a specifier like <span class="monospaced">B</span>, <span class="monospaced">I</span>, or <span class="monospaced">R</span>, or
is an inline string, like <span class="monospaced">`"foo"</span>. A backtick (<span class="monospaced">`</span>)
must precede each inline string. Each <span class="monospaced"><argi></span> must be of the
appropriate type for the corresponding specifier.</p></div>
<div class="paragraph"><p>SML <span class="monospaced">printf</span> is more powerful than its C counterpart in a number of
ways. In particular, the function produced by <span class="monospaced">printf</span> is a perfectly
ordinary SML function, and can be passed around, used multiple times,
etc. For example:</p></div>
<div class="listingblock">
<div class="content"><div class="highlight"><pre><span class="k">val</span><span class="w"> </span><span class="n">f</span><span class="p">:</span><span class="w"> </span><span class="n">int</span><span class="w"> </span><span class="p">-></span><span class="w"> </span><span class="n">bool</span><span class="w"> </span><span class="p">-></span><span class="w"> </span><span class="n">unit</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="n">printf</span><span class="w"> </span><span class="n">`</span><span class="s">"Int="</span><span class="n">I`</span><span class="s">" Bool="</span><span class="n">B`</span><span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="w"> </span><span class="n">$</span><span class="w"></span>
<span class="k">val</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="n">true</span><span class="w"></span>
<span class="k">val</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="n">false</span><span class="w"></span>
</pre></div></div></div>
<div class="paragraph"><p>The definition of <span class="monospaced">printf</span> is even careful to not print anything until
it is fully applied. So, examples like the following will work as
expected.</p></div>
<div class="listingblock">
<div class="content monospaced">
<pre>val f: int -> bool -> unit = printf `"Int="I`" Bool="B`"\n" $ 13
val () = f true
val () = f false</pre>
</div></div>
<div class="paragraph"><p>It is also easy to define new format specifiers. For example, suppose
we wanted format specifiers for characters and strings.</p></div>
<div class="listingblock">
<div class="content monospaced">
<pre>val C = fn z => spec Char.toString z
val S = fn z => spec (fn s => s) z</pre>
</div></div>
<div class="paragraph"><p>One can define format specifiers for more complex types, e.g. pairs of
integers.</p></div>
<div class="listingblock">
<div class="content monospaced">
<pre>val I2 =
fn z =>
spec (fn (i, j) =>
concat ["(", Int.toString i, ", ", Int.toString j, ")"])
z</pre>
</div></div>
<div class="paragraph"><p>Here’s an example use.</p></div>
<div class="listingblock">
<div class="content monospaced">
<pre>val () = printf `"Test "I2`" a string "S`"\n" $ (1, 2) "hello"</pre>
</div></div>
</div>
</div>
<div class="sect1">
<h2 id="_printf_via_a_href_fold_fold_a">Printf via <a href="Fold">Fold</a></h2>
<div class="sectionbody">
<div class="paragraph"><p><span class="monospaced">printf</span> is best viewed as a special case of variable-argument
<a href="Fold">Fold</a> that inductively builds a function as it processes its
arguments. Here is the definition of a <span class="monospaced">Printf</span> structure in terms of
fold. The structure is equivalent to the above one, except that it
uses the standard <span class="monospaced">$</span> instead of a specialized one.</p></div>
<div class="listingblock">
<div class="content"><div class="highlight"><pre><span class="k">structure</span><span class="w"> </span><span class="n">Printf</span><span class="w"> </span><span class="p">=</span><span class="w"></span>
<span class="w"> </span><span class="k">struct</span><span class="w"></span>
<span class="w"> </span><span class="k">fun</span><span class="w"> </span><span class="n">fprintf</span><span class="w"> </span><span class="n">out</span><span class="w"> </span><span class="p">=</span><span class="w"></span>
<span class="w"> </span><span class="n">Fold</span><span class="p">.</span><span class="n">fold</span><span class="w"> </span><span class="p">((</span><span class="n">out</span><span class="p">,</span><span class="w"> </span><span class="n">id</span><span class="p">),</span><span class="w"> </span><span class="k">fn</span><span class="w"> </span><span class="p">(_,</span><span class="w"> </span><span class="n">f</span><span class="p">)</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="p">(</span><span class="k">fn</span><span class="w"> </span><span class="n">p</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="n">p</span><span class="w"> </span><span class="p">())</span><span class="w"> </span><span class="n">ignore</span><span class="p">)</span><span class="w"></span>
<span class="w"> </span><span class="k">val</span><span class="w"> </span><span class="n">printf</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="k">fn</span><span class="w"> </span><span class="n">z</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="n">fprintf</span><span class="w"> </span><span class="n">TextIO</span><span class="p">.</span><span class="n">stdOut</span><span class="w"> </span><span class="n">z</span><span class="w"></span>
<span class="w"> </span><span class="k">fun</span><span class="w"> </span><span class="n">one</span><span class="w"> </span><span class="p">((</span><span class="n">out</span><span class="p">,</span><span class="w"> </span><span class="n">f</span><span class="p">),</span><span class="w"> </span><span class="n">make</span><span class="p">)</span><span class="w"> </span><span class="p">=</span><span class="w"></span>
<span class="w"> </span><span class="p">(</span><span class="n">out</span><span class="p">,</span><span class="w"> </span><span class="k">fn</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="p">=></span><span class="w"></span>
<span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="p">(</span><span class="k">fn</span><span class="w"> </span><span class="n">p</span><span class="w"> </span><span class="p">=></span><span class="w"></span>
<span class="w"> </span><span class="n">make</span><span class="w"> </span><span class="p">(</span><span class="k">fn</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="p">=></span><span class="w"></span>
<span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="p">(</span><span class="k">fn</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">(</span><span class="n">p</span><span class="w"> </span><span class="p">();</span><span class="w"> </span><span class="n">TextIO</span><span class="p">.</span><span class="n">output</span><span class="w"> </span><span class="p">(</span><span class="n">out</span><span class="p">,</span><span class="w"> </span><span class="n">s</span><span class="p">))))))</span><span class="w"></span>
<span class="w"> </span><span class="k">val</span><span class="w"> </span><span class="n">`</span><span class="w"> </span><span class="p">=</span><span class="w"></span>
<span class="w"> </span><span class="k">fn</span><span class="w"> </span><span class="n">z</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="n">Fold</span><span class="p">.</span><span class="n">step1</span><span class="w"> </span><span class="p">(</span><span class="k">fn</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="p">,</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="n">one</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="k">fn</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">s</span><span class="p">))</span><span class="w"> </span><span class="n">z</span><span class="w"></span>
<span class="w"> </span><span class="k">fun</span><span class="w"> </span><span class="n">spec</span><span class="w"> </span><span class="n">to</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="n">Fold</span><span class="p">.</span><span class="n">step0</span><span class="w"> </span><span class="p">(</span><span class="k">fn</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="n">one</span><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="k">fn</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="n">o</span><span class="w"> </span><span class="n">to</span><span class="p">))</span><span class="w"></span>
<span class="w"> </span><span class="k">val</span><span class="w"> </span><span class="n">B</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="k">fn</span><span class="w"> </span><span class="n">z</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="n">spec</span><span class="w"> </span><span class="n">Bool</span><span class="p">.</span><span class="n">toString</span><span class="w"> </span><span class="n">z</span><span class="w"></span>
<span class="w"> </span><span class="k">val</span><span class="w"> </span><span class="n">I</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="k">fn</span><span class="w"> </span><span class="n">z</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="n">spec</span><span class="w"> </span><span class="n">Int</span><span class="p">.</span><span class="n">toString</span><span class="w"> </span><span class="n">z</span><span class="w"></span>
<span class="w"> </span><span class="k">val</span><span class="w"> </span><span class="n">R</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="k">fn</span><span class="w"> </span><span class="n">z</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="n">spec</span><span class="w"> </span><span class="n">Real</span><span class="p">.</span><span class="n">toString</span><span class="w"> </span><span class="n">z</span><span class="w"></span>
<span class="w"> </span><span class="k">end</span><span class="w"></span>
</pre></div></div></div>
<div class="paragraph"><p>Viewing <span class="monospaced">printf</span> as a fold opens up a number of possibilities. For
example, one can name parts of format strings using the fold idiom for
naming sequences of steps.</p></div>
<div class="listingblock">
<div class="content monospaced">
<pre>val IB = fn u => Fold.fold u `"Int="I`" Bool="B
val () = printf IB`" "IB`"\n" $ 1 true 3 false</pre>
</div></div>
<div class="paragraph"><p>One can even parametrize over partial format strings.</p></div>
<div class="listingblock">
<div class="content monospaced">
<pre>fun XB X = fn u => Fold.fold u `"X="X`" Bool="B
val () = printf (XB I)`" "(XB R)`"\n" $ 1 true 2.0 false</pre>
</div></div>
</div>
</div>
<div class="sect1">
<h2 id="_also_see">Also see</h2>
<div class="sectionbody">
<div class="ulist"><ul>
<li>
<p>
<a href="PrintfGentle">PrintfGentle</a>
</p>
</li>
<li>
<p>
<a href="References#Danvy98"> Functional Unparsing</a>
</p>
</li>
</ul></div>
</div>
</div>
</div>
<div id="footnotes"><hr></div>
<div id="footer">
<div id="footer-text">
</div>
<div id="footer-badges">
</div>
</div>
</body>
</html>
|