<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="generator" content="AsciiDoc 8.6.10">
<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 20201002</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></span><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">=&gt;</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">=&gt;</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">=&gt;</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">=&gt;</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">=&gt;</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">=&gt;</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">=&gt;</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">=&gt;</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">=&gt;</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">=&gt;</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">=&gt;</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&#8217;s an example use.</p></div>
<div class="listingblock">
<div class="content"><div class="highlight"><pre><span></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">printf</span><span class="w"> </span><span class="n">`</span><span class="s">&quot;Int=&quot;</span><span class="n">I`</span><span class="s">&quot;  Bool=&quot;</span><span class="n">B`</span><span class="s">&quot;  Real=&quot;</span><span class="n">R`</span><span class="s">&quot;</span><span class="se">\n</span><span class="s">&quot;</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 &lt;spec1&gt; ... &lt;specn&gt; $ &lt;arg1&gt; ... &lt;argm&gt;</pre>
</div></div>
<div class="paragraph"><p>where each <span class="monospaced">&lt;speci&gt;</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">&grave;"foo"</span>.  A backtick (<span class="monospaced">&grave;</span>)
must precede each inline string.  Each <span class="monospaced">&lt;argi&gt;</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></span><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">-&gt;</span><span class="w"> </span><span class="n">bool</span><span class="w"> </span><span class="p">-&gt;</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">&quot;Int=&quot;</span><span class="n">I`</span><span class="s">&quot;  Bool=&quot;</span><span class="n">B`</span><span class="s">&quot;</span><span class="se">\n</span><span class="s">&quot;</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 -&gt; bool -&gt; 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 =&gt; spec Char.toString z
val S = fn z =&gt; spec (fn s =&gt; 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 =&gt;
   spec (fn (i, j) =&gt;
         concat ["(", Int.toString i, ", ", Int.toString j, ")"])
   z</pre>
</div></div>
<div class="paragraph"><p>Here&#8217;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></span><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">=&gt;</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">=&gt;</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">=&gt;</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">=&gt;</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">=&gt;</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">=&gt;</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">=&gt;</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">=&gt;</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">=&gt;</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">=&gt;</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">=&gt;</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">=&gt;</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">=&gt;</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">=&gt;</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">=&gt;</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 =&gt; 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 =&gt; 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>
