| CARVIEW |
Select Language
HTTP/2 200
server: GitHub.com
content-type: application/xml
last-modified: Wed, 19 May 2021 16:04:48 GMT
access-control-allow-origin: *
etag: W/"60a53720-1d580"
expires: Fri, 16 Jan 2026 01:43:51 GMT
cache-control: max-age=600
content-encoding: gzip
x-proxy-cache: MISS
x-github-request-id: 1C11:7ED56:E201:1472B:6969957E
accept-ranges: bytes
age: 0
date: Fri, 16 Jan 2026 01:33:51 GMT
via: 1.1 varnish
x-served-by: cache-bom-vanm7210038-BOM
x-cache: MISS
x-cache-hits: 0
x-timer: S1768527232.695015,VS0,VE242
vary: Accept-Encoding
x-fastly-request-id: 731227c17696e041620be0e5657868d06cea8ec8
content-length: 14331
Functional Programming at the University of Kansas
The Functional Programming Group at the University of Kansas applies and extends functional programming technologies to the diverse areas of building computer systems, high-performance computing, information assurance, and telemetry.
https://www.ittc.ku.edu/csdl/fpg/blog
Wed, 19 May 2021 16:04:44 +0000
Wed, 19 May 2021 16:04:44 +0000
Jekyll v3.9.0
-
The Remote JSON library
<p><a href="https://www.jsonrpc.org/">JSON-RPC</a> is a simple and well supported protocol for
remote procedure calls over HTTP,
supporting both synchronous remote methods calls
and asynchronous notifications. We want to access JSON-RPC from Haskell,
but in a principled way. This blog post discusses the design and user-facing interface
of <a href="https://hackage.haskell.org/package/remote-json"><code class="language-plaintext highlighter-rouge">remote-json</code></a>,
a new library for JSON-RPC that makes use of the
<a href="/practice/remotemonad/">remote monad design pattern</a></p>
<!--MORE-->
<p>To give an example from the specification, consider calling
a method <code class="language-plaintext highlighter-rouge">subtract</code>, with the arguments <code class="language-plaintext highlighter-rouge">42</code> and <code class="language-plaintext highlighter-rouge">23</code>.</p>
<figure class="highlight"><pre><code class="language-json" data-lang="json"><span class="err">--></span><span class="w"> </span><span class="p">{</span><span class="nl">"jsonrpc"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2.0"</span><span class="p">,</span><span class="w"> </span><span class="nl">"method"</span><span class="p">:</span><span class="w"> </span><span class="s2">"subtract"</span><span class="p">,</span><span class="w"> </span><span class="nl">"params"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="mi">42</span><span class="p">,</span><span class="w"> </span><span class="mi">23</span><span class="p">],</span><span class="w"> </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">}</span><span class="w">
</span><span class="err"><--</span><span class="w"> </span><span class="p">{</span><span class="nl">"jsonrpc"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2.0"</span><span class="p">,</span><span class="w"> </span><span class="nl">"result"</span><span class="p">:</span><span class="w"> </span><span class="mi">19</span><span class="p">,</span><span class="w"> </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">}</span></code></pre></figure>
<p>Here, <code class="language-plaintext highlighter-rouge">--></code> is the data sent from client to server, and <code class="language-plaintext highlighter-rouge"><--</code> is what the
server responds. The packet being sent is a simple JSON object, as is the
reply from the server.</p>
<p>The JSON-RPC protocol supports batching
(sending several method calls and notifications at the same time) and
is easy to debug, because it is straightforward to read JSON structures.
Furthermore, by using JSON-RPC we can implement our clients
in Haskell, and be agnostic about what language or framework the server is written in.</p>
<p>There are at least five existing Haskell libraries that support
JSON-RPC. So why a new library?
We wanted to build our own because we saw a
way of simplifying the API considerably, while still allowing
access to all the capabilities of JSON-RPC. Specifically,
by using the remote monad design pattern
<strong>we can automate taking advantage of the batch capability</strong>,
amortizing the cost of the remote call. Rather than have
separate entry points for batched and singleton calls,
a single entry point can provide both batched and singleton calls.
The library also acts as a case study of using the remote monad.</p>
<h2 id="basic-design-of-the-json-rpc-api">Basic Design of the JSON-RPC API</h2>
<p>We center our design around the <code class="language-plaintext highlighter-rouge">RPC</code> monad.</p>
<figure class="highlight"><pre><code class="language-haskell" data-lang="haskell"><span class="c1">-- The monad</span>
<span class="kr">data</span> <span class="kt">RPC</span> <span class="o">::</span> <span class="o">*</span> <span class="o">-></span> <span class="o">*</span> <span class="kr">deriving</span> <span class="p">(</span><span class="kt">Monad</span><span class="p">,</span> <span class="kt">Applicative</span><span class="p">,</span> <span class="kt">Functor</span><span class="p">)</span>
<span class="c1">-- The primitives</span>
<span class="n">method</span> <span class="o">::</span> <span class="kt">FromJSON</span> <span class="n">a</span> <span class="o">=></span> <span class="kt">Text</span> <span class="o">-></span> <span class="kt">Args</span> <span class="o">-></span> <span class="kt">RPC</span> <span class="n">a</span>
<span class="n">notification</span> <span class="o">::</span> <span class="kt">Text</span> <span class="o">-></span> <span class="kt">Args</span> <span class="o">-></span> <span class="kt">RPC</span> <span class="nb">()</span>
<span class="c1">-- the remote send</span>
<span class="n">send</span> <span class="o">::</span> <span class="kt">Session</span> <span class="o">-></span> <span class="kt">RPC</span> <span class="n">a</span> <span class="o">-></span> <span class="kt">IO</span> <span class="n">a</span></code></pre></figure>
<p>This is a classical remote monad design - a restricted monad, a small number of primitives
for this monad, and a <code class="language-plaintext highlighter-rouge">send</code> function. <code class="language-plaintext highlighter-rouge">Session</code> is an abstract handle to the web server
we want to talk to; we’ll come back to how to generate a <code class="language-plaintext highlighter-rouge">Session</code> shortly.</p>
<p>This API gives an (aeson) Value-based access to JSON-RPC. Adding specific primitives gives
stronger typing. As an example, consider providing <code class="language-plaintext highlighter-rouge">say</code> notifications, that make the remote
server say things, a <code class="language-plaintext highlighter-rouge">temperature</code> method that returns the remote server’s temperature,
and an <code class="language-plaintext highlighter-rouge">uptime</code> method that returns the uptime of a specific service.</p>
<figure class="highlight"><pre><code class="language-haskell" data-lang="haskell"><span class="n">say</span> <span class="o">::</span> <span class="kt">Text</span> <span class="o">-></span> <span class="kt">RPC</span> <span class="nb">()</span>
<span class="n">say</span> <span class="n">msg</span> <span class="o">=</span> <span class="n">notification</span> <span class="s">"say"</span> <span class="o">$</span> <span class="kt">List</span> <span class="p">[</span><span class="kt">String</span> <span class="n">msg</span><span class="p">]</span>
<span class="n">temperature</span> <span class="o">::</span> <span class="kt">RPC</span> <span class="kt">Int</span>
<span class="n">temperature</span> <span class="o">=</span> <span class="n">method</span> <span class="s">"temperature"</span> <span class="kt">None</span>
<span class="n">uptime</span> <span class="o">::</span> <span class="kt">Text</span> <span class="o">-></span> <span class="kt">RPC</span> <span class="kt">Double</span>
<span class="n">uptime</span> <span class="n">nm</span> <span class="o">=</span> <span class="n">method</span> <span class="s">"uptime"</span> <span class="o">$</span> <span class="kt">List</span> <span class="p">[</span><span class="kt">String</span> <span class="n">nm</span><span class="p">]</span>
<span class="c1">-- provided utilities for generating Args, and parsing the Value result of a method.</span>
<span class="kt">List</span> <span class="o">::</span> <span class="p">[</span><span class="kt">Value</span><span class="p">]</span> <span class="o">-></span> <span class="kt">Args</span>
<span class="kt">None</span> <span class="o">::</span> <span class="kt">Args</span></code></pre></figure>
<p>As an example we have our typed API for <code class="language-plaintext highlighter-rouge">temperature</code>, <code class="language-plaintext highlighter-rouge">uptime</code> and <code class="language-plaintext highlighter-rouge">say</code>, saying “Hello, World!”,
getting the temperature, and the uptime of “orange”.</p>
<figure class="highlight"><pre><code class="language-haskell" data-lang="haskell"><span class="n">example</span> <span class="o">::</span> <span class="kt">Session</span> <span class="o">-></span> <span class="kt">IO</span> <span class="nb">()</span>
<span class="n">example</span> <span class="n">s</span> <span class="o">=</span> <span class="kr">do</span>
<span class="p">(</span><span class="n">t</span><span class="p">,</span><span class="n">u</span><span class="p">)</span> <span class="o"><-</span> <span class="n">send</span> <span class="n">s</span> <span class="o">$</span> <span class="kr">do</span>
<span class="n">say</span> <span class="s">"Hello, "</span>
<span class="n">t</span> <span class="o"><-</span> <span class="n">temperature</span>
<span class="n">say</span> <span class="s">"World!"</span>
<span class="n">u</span> <span class="o"><-</span> <span class="n">uptime</span> <span class="s">"orange"</span>
<span class="n">return</span> <span class="p">(</span><span class="n">t</span><span class="p">,</span><span class="n">u</span><span class="p">)</span>
<span class="n">print</span> <span class="n">t</span>
<span class="n">print</span> <span class="n">u</span> </code></pre></figure>
<p><code class="language-plaintext highlighter-rouge">send</code> can be used
multiple times if needed, acting as translation between our <code class="language-plaintext highlighter-rouge">IO</code> monad, and the remote <code class="language-plaintext highlighter-rouge">RPC</code> monad.
Furthermore, the following JSON-RPC interaction with the server would be a valid
trace of interactions for the above example.</p>
<figure class="highlight"><pre><code class="language-json" data-lang="json"><span class="err">--></span><span class="w"> </span><span class="p">{</span><span class="nl">"jsonrpc"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2.0"</span><span class="p">,</span><span class="w"> </span><span class="nl">"method"</span><span class="p">:</span><span class="w"> </span><span class="s2">"say"</span><span class="p">,</span><span class="w"> </span><span class="nl">"params"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"Hello, "</span><span class="p">]}</span><span class="w">
</span><span class="err">//</span><span class="w"> </span><span class="err">No</span><span class="w"> </span><span class="err">reply</span><span class="w">
</span><span class="err">--></span><span class="w"> </span><span class="p">{</span><span class="nl">"jsonrpc"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2.0"</span><span class="p">,</span><span class="w"> </span><span class="nl">"method"</span><span class="p">:</span><span class="w"> </span><span class="s2">"temperature"</span><span class="p">,</span><span class="w"> </span><span class="err">id:</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="p">}</span><span class="w">
</span><span class="err"><--</span><span class="w"> </span><span class="p">{</span><span class="nl">"jsonrpc"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2.0"</span><span class="p">,</span><span class="w"> </span><span class="nl">"result"</span><span class="p">:</span><span class="w"> </span><span class="mi">99</span><span class="p">,</span><span class="w"> </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">}</span><span class="w">
</span><span class="err">--></span><span class="w"> </span><span class="p">{</span><span class="nl">"jsonrpc"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2.0"</span><span class="p">,</span><span class="w"> </span><span class="nl">"method"</span><span class="p">:</span><span class="w"> </span><span class="s2">"say"</span><span class="p">,</span><span class="w"> </span><span class="nl">"params"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"World "</span><span class="p">]}</span><span class="w">
</span><span class="err">//</span><span class="w"> </span><span class="err">No</span><span class="w"> </span><span class="err">reply</span><span class="w">
</span><span class="err">--></span><span class="w"> </span><span class="p">{</span><span class="nl">"jsonrpc"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2.0"</span><span class="p">,</span><span class="w"> </span><span class="nl">"method"</span><span class="p">:</span><span class="w"> </span><span class="s2">"uptime"</span><span class="p">,</span><span class="w"> </span><span class="nl">"params"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"orange"</span><span class="p">],</span><span class="w"> </span><span class="err">id:</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="p">}</span><span class="w">
</span><span class="err"><--</span><span class="w"> </span><span class="p">{</span><span class="nl">"jsonrpc"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2.0"</span><span class="p">,</span><span class="w"> </span><span class="nl">"result"</span><span class="p">:</span><span class="w"> </span><span class="mf">3.14</span><span class="p">,</span><span class="w"> </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">}</span></code></pre></figure>
<p>In this interaction:</p>
<ul>
<li><strong>notifications</strong> are methods without id’s, and do not have replies, and</li>
<li><strong>methods</strong> use an id to tag a result.</li>
</ul>
<p>This usage is reasonable, <strong>but we can do better</strong>. We want to bundle together
notifications and methods, to amortize the cost of network traffic, but
without comprising the API. Specifically, we want users to just write code
using <code class="language-plaintext highlighter-rouge">send</code>, and the RPC library to figure out the best bundling possible.</p>
<h2 id="the-remote-monad">The Remote Monad</h2>
<p>In the remote monad theory, there are two key concepts:</p>
<ul>
<li>Splitting the monadic primitives into commands and procedures.
<ul>
<li><strong>Commands</strong> do not have a result value (typically <code class="language-plaintext highlighter-rouge">()</code> in Haskell), and</li>
<li><strong>Procedures</strong> have a result.</li>
</ul>
<p>There are restrictions on commands and procedures, specifically that they
must be serializable.</p>
</li>
<li>Choosing a bundling strategy. There are two strategies that were documented in the original paper
and one new bundling strategy that we are investigating.
<ul>
<li><strong>Weak</strong> - a bundle of a single command or a single procedure, or</li>
<li><strong>Strong</strong> - a bundle of a sequence of commands, optionally terminated by a procedure, or</li>
<li><strong>Applicative</strong> - a bundle of a sequence of commands and procedures, held together using an applicative functor.</li>
</ul>
</li>
</ul>
<p>By factoring our primitives into commands and procedures, we can automatically split up a monadic computation
into maximal bundles, and then use a transport layer to send, execute and get
the result from each bundle. The good news is there is a library, called
the <a href="https://hackage.haskell.org/package/remote-monad"><code class="language-plaintext highlighter-rouge">remote-monad</code></a>,
that has a plug-and-play API. If we provide the best bundling transport we can,
then the library can pick the best way of splitting up the monadic computation
into our bundles.</p>
<p>Considering JSON-RPC, the concept of notification and method map straight onto the
remote monad concepts of commands and procedures. This makes things straightforward.</p>
<h3 id="weak-bundles">Weak Bundles</h3>
<p>You can create a JSON-RPC instance using <code class="language-plaintext highlighter-rouge">weakSession</code>,
which takes an encoding of <strong>how</strong> to send values to a
remote server, and returns a <code class="language-plaintext highlighter-rouge">Session</code>.</p>
<figure class="highlight"><pre><code class="language-haskell" data-lang="haskell"><span class="n">weakSession</span> <span class="o">::</span> <span class="p">(</span><span class="kt">SendAPI</span> <span class="o">:~></span> <span class="kt">IO</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Session</span></code></pre></figure>
<p><code class="language-plaintext highlighter-rouge">SendAPI :~> IO</code> is a natural transformation.</p>
<figure class="highlight"><pre><code class="language-haskell" data-lang="haskell"><span class="kr">newtype</span> <span class="n">f</span> <span class="o">:~></span> <span class="n">g</span> <span class="o">=</span> <span class="kt">Nat</span> <span class="p">(</span><span class="err">∀</span> <span class="n">a</span><span class="o">.</span> <span class="n">f</span> <span class="n">a</span> <span class="o">-></span> <span class="n">g</span> <span class="n">a</span><span class="p">)</span></code></pre></figure>
<p>Specifically, <code class="language-plaintext highlighter-rouge">SendAPI :~> IO</code> is a functor
morphism between <code class="language-plaintext highlighter-rouge">SendAPI</code> and <code class="language-plaintext highlighter-rouge">IO</code>,
and isomorphic to <code class="language-plaintext highlighter-rouge">∀ a. SendAPI a -> IO a</code>.
Operationally,
this transformation is how you <strong>run</strong> <code class="language-plaintext highlighter-rouge">SendAPI</code>,
using <code class="language-plaintext highlighter-rouge">IO</code>. <code class="language-plaintext highlighter-rouge">SendAPI</code> is a deep embedding of
both synchronous and asynchronous communications of JSON <code class="language-plaintext highlighter-rouge">Value</code>.</p>
<figure class="highlight"><pre><code class="language-haskell" data-lang="haskell"><span class="kr">data</span> <span class="kt">SendAPI</span> <span class="o">::</span> <span class="o">*</span> <span class="o">-></span> <span class="o">*</span> <span class="kr">where</span>
<span class="kt">Sync</span> <span class="o">::</span> <span class="kt">Value</span> <span class="o">-></span> <span class="kt">SendAPI</span> <span class="kt">Value</span>
<span class="kt">Async</span> <span class="o">::</span> <span class="kt">Value</span> <span class="o">-></span> <span class="kt">SendAPI</span> <span class="nb">()</span> </code></pre></figure>
<p>So, calling <code class="language-plaintext highlighter-rouge">send</code> with the <code class="language-plaintext highlighter-rouge">RPC</code> monadic remote commands
factors up the specifics commands, and calls the
natural transformation argument with either <code class="language-plaintext highlighter-rouge">Sync</code> or <code class="language-plaintext highlighter-rouge">ASync</code>.
With the <code class="language-plaintext highlighter-rouge">weakSession</code>, every primitive causes its own
<code class="language-plaintext highlighter-rouge">Sync</code> or <code class="language-plaintext highlighter-rouge">ASync</code>; there is no complex bundling.</p>
<p>You can write your own matcher for <code class="language-plaintext highlighter-rouge">SendAPI</code>, or use
<a href="https://hackage.haskell.org/package/remote-json-client"><code class="language-plaintext highlighter-rouge">remote-json-client</code></a>,
which provides a function that, when given a URL, returns
the <code class="language-plaintext highlighter-rouge">SendAPI</code> to <code class="language-plaintext highlighter-rouge">IO</code> natural transformation using the
<a href="https://hackage.haskell.org/package/wreq"><code class="language-plaintext highlighter-rouge">wreq</code></a> library.</p>
<figure class="highlight"><pre><code class="language-haskell" data-lang="haskell"><span class="n">clientSendAPI</span> <span class="o">::</span> <span class="kt">String</span> <span class="o">-></span> <span class="p">(</span><span class="kt">SendAPI</span> <span class="o">:~></span> <span class="kt">IO</span><span class="p">)</span></code></pre></figure>
<p>Putting this together, we get</p>
<figure class="highlight"><pre><code class="language-haskell" data-lang="haskell"><span class="n">main</span> <span class="o">::</span> <span class="kt">IO</span> <span class="nb">()</span>
<span class="n">main</span> <span class="o">=</span> <span class="kr">do</span>
<span class="kr">let</span> <span class="n">s</span> <span class="o">=</span> <span class="n">weakSession</span> <span class="p">(</span><span class="n">clientSendAPI</span> <span class="s">"https://www.wibble.com/wobble"</span><span class="p">)</span>
<span class="p">(</span><span class="n">t</span><span class="p">,</span><span class="n">u</span><span class="p">)</span> <span class="o"><-</span> <span class="n">send</span> <span class="n">s</span> <span class="o">$</span> <span class="kr">do</span>
<span class="n">say</span> <span class="s">"Hello, "</span>
<span class="n">t</span> <span class="o"><-</span> <span class="n">temperature</span>
<span class="n">say</span> <span class="s">"World!"</span>
<span class="n">u</span> <span class="o"><-</span> <span class="n">uptime</span> <span class="s">"orange"</span>
<span class="n">return</span> <span class="p">(</span><span class="n">t</span><span class="p">,</span><span class="n">u</span><span class="p">)</span>
<span class="n">print</span> <span class="n">t</span>
<span class="n">print</span> <span class="n">u</span> </code></pre></figure>
<p>Having selected the weak remote monad, we have the weakest JSON-RPC interaction -
four transactions.</p>
<figure class="highlight"><pre><code class="language-json" data-lang="json"><span class="err">//</span><span class="w"> </span><span class="err">(</span><span class="mi">1</span><span class="err">)</span><span class="w">
</span><span class="err">--></span><span class="w"> </span><span class="p">{</span><span class="nl">"jsonrpc"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2.0"</span><span class="p">,</span><span class="w"> </span><span class="nl">"method"</span><span class="p">:</span><span class="w"> </span><span class="s2">"say"</span><span class="p">,</span><span class="w"> </span><span class="nl">"params"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"Hello, "</span><span class="p">]}</span><span class="w">
</span><span class="err">//</span><span class="w"> </span><span class="err">No</span><span class="w"> </span><span class="err">reply</span><span class="w">
</span><span class="err">//</span><span class="w"> </span><span class="err">(</span><span class="mi">2</span><span class="err">)</span><span class="w">
</span><span class="err">--></span><span class="w"> </span><span class="p">{</span><span class="nl">"jsonrpc"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2.0"</span><span class="p">,</span><span class="w"> </span><span class="nl">"method"</span><span class="p">:</span><span class="w"> </span><span class="s2">"temperature"</span><span class="p">,</span><span class="w"> </span><span class="err">id:</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="p">}</span><span class="w">
</span><span class="err"><--</span><span class="w"> </span><span class="p">{</span><span class="nl">"jsonrpc"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2.0"</span><span class="p">,</span><span class="w"> </span><span class="nl">"result"</span><span class="p">:</span><span class="w"> </span><span class="mi">99</span><span class="p">,</span><span class="w"> </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">}</span><span class="w">
</span><span class="err">//</span><span class="w"> </span><span class="err">(</span><span class="mi">3</span><span class="err">)</span><span class="w">
</span><span class="err">--></span><span class="w"> </span><span class="p">{</span><span class="nl">"jsonrpc"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2.0"</span><span class="p">,</span><span class="w"> </span><span class="nl">"method"</span><span class="p">:</span><span class="w"> </span><span class="s2">"say"</span><span class="p">,</span><span class="w"> </span><span class="nl">"params"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"World "</span><span class="p">]}</span><span class="w">
</span><span class="err">//</span><span class="w"> </span><span class="err">No</span><span class="w"> </span><span class="err">reply</span><span class="w">
</span><span class="err">//</span><span class="w"> </span><span class="err">(</span><span class="mi">4</span><span class="err">)</span><span class="w">
</span><span class="err">--></span><span class="w"> </span><span class="p">{</span><span class="nl">"jsonrpc"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2.0"</span><span class="p">,</span><span class="w"> </span><span class="nl">"method"</span><span class="p">:</span><span class="w"> </span><span class="s2">"uptime"</span><span class="p">,</span><span class="w"> </span><span class="nl">"params"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"orange"</span><span class="p">],</span><span class="w"> </span><span class="err">id:</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="p">}</span><span class="w">
</span><span class="err"><--</span><span class="w"> </span><span class="p">{</span><span class="nl">"jsonrpc"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2.0"</span><span class="p">,</span><span class="w"> </span><span class="nl">"result"</span><span class="p">:</span><span class="w"> </span><span class="mf">3.14</span><span class="p">,</span><span class="w"> </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">}</span></code></pre></figure>
<h3 id="strong-bundles">Strong Bundles</h3>
<p>The strong remote monad bundles together commands, where possible, to amortize
the cost of the remote call. In our example above, we have two notifications,
and a method call. We want to combine them together. We do so by using the
<code class="language-plaintext highlighter-rouge">strongSession</code> combinator.</p>
<figure class="highlight"><pre><code class="language-haskell" data-lang="haskell"><span class="n">main</span> <span class="o">::</span> <span class="kt">IO</span> <span class="nb">()</span>
<span class="n">main</span> <span class="o">=</span> <span class="kr">do</span>
<span class="kr">let</span> <span class="n">s</span> <span class="o">=</span> <span class="n">strongSession</span> <span class="p">(</span><span class="n">clientSendAPI</span> <span class="s">"https://www.wibble.com/wobble"</span><span class="p">)</span>
<span class="p">(</span><span class="n">t</span><span class="p">,</span><span class="n">u</span><span class="p">)</span> <span class="o"><-</span> <span class="n">send</span> <span class="n">s</span> <span class="o">$</span> <span class="kr">do</span>
<span class="n">say</span> <span class="s">"Hello, "</span>
<span class="n">t</span> <span class="o"><-</span> <span class="n">temperature</span>
<span class="n">say</span> <span class="s">"World!"</span>
<span class="n">u</span> <span class="o"><-</span> <span class="n">uptime</span> <span class="s">"orange"</span>
<span class="n">return</span> <span class="p">(</span><span class="n">t</span><span class="p">,</span><span class="n">u</span><span class="p">)</span>
<span class="n">print</span> <span class="n">t</span>
<span class="n">print</span> <span class="n">u</span> </code></pre></figure>
<p>Now, we get a two transactions, which conforms to the JSON-RPC specification.</p>
<figure class="highlight"><pre><code class="language-json" data-lang="json"><span class="err">--></span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">{</span><span class="nl">"jsonrpc"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2.0"</span><span class="p">,</span><span class="w"> </span><span class="nl">"method"</span><span class="p">:</span><span class="w"> </span><span class="s2">"say"</span><span class="p">,</span><span class="w"> </span><span class="nl">"params"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"Hello. "</span><span class="p">]}</span><span class="w">
</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="nl">"jsonrpc"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2.0"</span><span class="p">,</span><span class="w"> </span><span class="nl">"method"</span><span class="p">:</span><span class="w"> </span><span class="s2">"temperature"</span><span class="p">,</span><span class="w"> </span><span class="err">id:</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="p">}</span><span class="w">
</span><span class="p">]</span><span class="w">
</span><span class="err"><--</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">{</span><span class="nl">"jsonrpc"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2.0"</span><span class="p">,</span><span class="w"> </span><span class="nl">"result"</span><span class="p">:</span><span class="w"> </span><span class="mi">99</span><span class="p">,</span><span class="w"> </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">}</span><span class="w">
</span><span class="p">]</span><span class="w">
</span><span class="err">--></span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">{</span><span class="nl">"jsonrpc"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2.0"</span><span class="p">,</span><span class="w"> </span><span class="nl">"method"</span><span class="p">:</span><span class="w"> </span><span class="s2">"say"</span><span class="p">,</span><span class="w"> </span><span class="nl">"params"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"World "</span><span class="p">]}</span><span class="w">
</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="nl">"jsonrpc"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2.0"</span><span class="p">,</span><span class="w"> </span><span class="nl">"method"</span><span class="p">:</span><span class="w"> </span><span class="s2">"uptime"</span><span class="p">,</span><span class="w"> </span><span class="nl">"params"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"orange"</span><span class="p">],</span><span class="w"> </span><span class="err">id:</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="p">}</span><span class="w">
</span><span class="p">]</span><span class="w">
</span><span class="err"><--</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">{</span><span class="nl">"jsonrpc"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2.0"</span><span class="p">,</span><span class="w"> </span><span class="nl">"result"</span><span class="p">:</span><span class="w"> </span><span class="mf">3.14</span><span class="p">,</span><span class="w"> </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">}</span><span class="w">
</span><span class="p">]</span></code></pre></figure>
<p>The same RPC <code class="language-plaintext highlighter-rouge">send</code> command gives better bundling, solely from changing the
strategy from a <code class="language-plaintext highlighter-rouge">weakSession</code> to a <code class="language-plaintext highlighter-rouge">strongSession</code>.
Now, the JSON-RPC specification says</p>
<ul>
<li>“The Server MAY process a batch rpc call as a set of concurrent tasks,
processing them in any order and with any width of parallelism.”</li>
</ul>
<p>This is where it gets interesting. The RPC monad reflects the concurrency
policy of the the server, up to method calls. If you wish to insist on
sequentiality, then use the <code class="language-plaintext highlighter-rouge">weakSession</code>.</p>
<h2 id="applicative-bundles">Applicative Bundles</h2>
<p>One obvious question is “can we improve
this reflection of concurrency, and bundle method calls as well as notification?”
Yes! For example, <a href="https://github.com/facebook/Haxl">Haxl</a>,
a Haskell DSL for database access,
uses an even stronger bundling strategy, which we call the applicative bundling strategy.
In this strategy, methods that are expressed using applicative functors can be
bundled together, as well as commands that can already be bundled.</p>
<p>The <code class="language-plaintext highlighter-rouge">remote-json</code> library supports applicative bundling. Again, the details
are abstracted by the library, and again the monad and applicative functor
reflect the concurrency semantics of the server. We’ve added a new remote
command <code class="language-plaintext highlighter-rouge">uptime</code> that returns the uptime of a specific machine, to aid
the demonstration of batching, and reworked the computation to use applicative.</p>
<figure class="highlight"><pre><code class="language-haskell" data-lang="haskell"><span class="n">main</span> <span class="o">::</span> <span class="kt">IO</span> <span class="nb">()</span>
<span class="n">main</span> <span class="o">=</span> <span class="kr">do</span>
<span class="kr">let</span> <span class="n">s</span> <span class="o">=</span> <span class="n">applicativeSession</span> <span class="p">(</span><span class="n">clientSendAPI</span> <span class="s">"https://www.wibble.com/wobble"</span><span class="p">)</span>
<span class="p">(</span><span class="n">t</span><span class="p">,</span><span class="n">u</span><span class="p">)</span> <span class="o"><-</span> <span class="n">send</span> <span class="n">s</span> <span class="o">$</span>
<span class="n">say</span> <span class="s">"Hello, "</span> <span class="o">*></span>
<span class="p">(</span><span class="n">pure</span> <span class="p">(,)</span> <span class="o"><*></span> <span class="n">temperature</span>
<span class="o"><*</span> <span class="n">say</span> <span class="s">"World!"</span>
<span class="o"><*></span> <span class="n">uptime</span> <span class="s">"orange"</span><span class="p">)</span>
<span class="n">print</span> <span class="p">(</span><span class="n">t</span><span class="p">,</span><span class="n">u</span><span class="p">)</span></code></pre></figure>
<p>The packet now includes two notifications and two methods. (Remember,
a notification is a JSON-RPC method that has no id tag.)</p>
<figure class="highlight"><pre><code class="language-json" data-lang="json"><span class="err">--></span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">{</span><span class="nl">"jsonrpc"</span><span class="p">:</span><span class="s2">"2.0"</span><span class="p">,</span><span class="nl">"params"</span><span class="p">:[</span><span class="s2">"Hello,"</span><span class="p">],</span><span class="nl">"method"</span><span class="p">:</span><span class="s2">"say"</span><span class="p">}</span><span class="w">
</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="nl">"jsonrpc"</span><span class="p">:</span><span class="s2">"2.0"</span><span class="p">,</span><span class="nl">"method"</span><span class="p">:</span><span class="s2">"temperature"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="mi">2</span><span class="p">},</span><span class="w">
</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="nl">"jsonrpc"</span><span class="p">:</span><span class="s2">"2.0"</span><span class="p">,</span><span class="nl">"params"</span><span class="p">:[</span><span class="s2">"World!"</span><span class="p">],</span><span class="nl">"method"</span><span class="p">:</span><span class="s2">"say"</span><span class="p">}</span><span class="w">
</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="nl">"jsonrpc"</span><span class="p">:</span><span class="s2">"2.0"</span><span class="p">,</span><span class="nl">"params"</span><span class="p">:[</span><span class="s2">"orange"</span><span class="p">],</span><span class="nl">"method"</span><span class="p">:</span><span class="s2">"uptime"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="mi">1</span><span class="p">}</span><span class="w">
</span><span class="p">]</span><span class="w">
</span><span class="err"><--</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">{</span><span class="nl">"result"</span><span class="p">:</span><span class="mi">65</span><span class="p">,</span><span class="nl">"jsonrpc"</span><span class="p">:</span><span class="s2">"2.0"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="mi">2</span><span class="p">}</span><span class="w">
</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="nl">"result"</span><span class="p">:</span><span class="mf">68.28</span><span class="p">,</span><span class="nl">"jsonrpc"</span><span class="p">:</span><span class="s2">"2.0"</span><span class="p">,</span><span class="nl">"id"</span><span class="p">:</span><span class="mi">1</span><span class="p">}</span><span class="w">
</span><span class="p">]</span></code></pre></figure>
<p>When <a href="https://ghc.haskell.org/trac/ghc/wiki/ApplicativeDo">Applicative do</a>
arrives with GHC 8, we can take immediate advantage of this, and start using
<code class="language-plaintext highlighter-rouge">do</code>-notation to write out remote applicative functors. In fact, we can
interperse our usage of monads and applicative <code class="language-plaintext highlighter-rouge">RPC</code>, and the package
will choose the best bundling it can for the specific strategy.</p>
<h2 id="summary">Summary</h2>
<p>We have written a remote monad for JSON-RPC. The library automatically bundles
inside <code class="language-plaintext highlighter-rouge">send</code> to attempt to amortize the cost of the remote procedure call. It
simplifies making multiple requests, without resorting to specialized variants
of <code class="language-plaintext highlighter-rouge">send</code>.
These idea has been used before. For example,
Haxl lead the charge of using the
applicative bundling strategy,
and Oleg implemented a remote monad in OCaml several years ago.
And we’ve been using predecessors to <code class="language-plaintext highlighter-rouge">remote-json</code> for several years now.
Feel free to try <code class="language-plaintext highlighter-rouge">remote-json</code>, and if you are adventurous, <code class="language-plaintext highlighter-rouge">remote-monad</code>.
Or roll your own remote monad. Once you know the pattern, it’s quite straightforward!</p>
<p>In a future blog post, we will look at our JSON-RPC server.</p>
<p>Enjoy!</p>
<p>Justin Dawson and Andy Gill</p>
<h2 id="resources-and-related-work">Resources and Related Work</h2>
<ul>
<li><a href="https://hackage.haskell.org/package/remote-json"><code class="language-plaintext highlighter-rouge">remote-json</code></a></li>
<li><a href="https://hackage.haskell.org/package/remote-monad"><code class="language-plaintext highlighter-rouge">remote-monad</code></a></li>
<li><a href="https://hackage.haskell.org/package/haxl"><code class="language-plaintext highlighter-rouge">haxl</code></a></li>
<li><a href="https://dl.acm.org/citation.cfm?id=2628144">There is no fork:</a>
an abstraction for efficient, concurrent, and concise data access,
Simon Marlow and Louis Brandy and Jonathan Coens and Jon Purdy.</li>
<li><a href="https://okmij.org/ftp/meta-future/">Semi-implicit batched remote code execution as staging</a>, Oleg Kiselyov</li>
<li>The <a href="/practice/remotemonad/">remote monad design pattern</a></li>
</ul>
<h3 id="existing-json-rpc-packages">Existing JSON-RPC packages</h3>
<ul>
<li><a href="https://hackage.haskell.org/package/json-rpc"><code class="language-plaintext highlighter-rouge">json-rpc</code></a> by Jean-Pierre Rupp</li>
<li><a href="https://hackage.haskell.org/package/json-rpc-client"><code class="language-plaintext highlighter-rouge">json-rpc-client</code></a> by Kristen Kozak</li>
<li><a href="https://hackage.haskell.org/package/jsonrpc-conduit"><code class="language-plaintext highlighter-rouge">jsonrpc-conduit</code></a> by Gabriele Sales</li>
<li><a href="https://hackage.haskell.org/package/colchis"><code class="language-plaintext highlighter-rouge">colchis</code></a> by Daniel Díaz Carrete</li>
<li><a href="https://hackage.haskell.org/package/jmacro-rpc"><code class="language-plaintext highlighter-rouge">jmacro-rpc</code></a> by Gershom Bazerman</li>
</ul>
<h3 id="our-publications-about-the-remote-monad">Our Publications about the Remote Monad</h3>
<ul class="citation-list">
<li><p>A. Gill, N. Sculthorpe, J. Dawson, A. Eskilson, A. Farmer, M. Grebe,
J. Rosenbluth, R. Scott, and J. Stanton, “<a href="/papers/Gill-15-RemoteMonad">The remote monad design
pattern</a>,” in <span><em>Proceedings of the 8th ACM SIGPLAN Symposium on
Haskell</em></span>, (New York, NY, USA), pp. 59–70, ACM, 2015.</p>
</li>
</ul>
<ul class="citation-list">
<li><p>M. Grebe and A. Gill, “<a href="/papers/Grebe-16-Haskino"><span>H</span>askino: A remote monad for
programming the <span>A</span>rduino</a>,” in <span><em>Practical Aspects of
Declarative Languages</em></span>, Lecture Notes in Computer Science, 2016.</p>
</li>
</ul>
Tue, 09 Feb 2016 00:00:00 +0000
https://www.ittc.ku.edu/csdl/fpg/2016/02/09/remote-json/
https://www.ittc.ku.edu/csdl/fpg/2016/02/09/remote-json/
-
Shells and the Remote Monad Design Pattern
<p>The <strong>remote monad design pattern</strong> is a way of encapsulating external
monadic capabilities.
The idea is that, rather than directly call a remote or external procedure,
we instead give the external procedure call a service-specific monadic
type, and invoke the external procedure call using a monadic “send” function.
Specifically, a <strong>remote monad</strong> is a monad that has its evaluation function in
a remote location, outside the local runtime system.
This blog article, the first in a series, examines reflecting
shell commands for accessing external data from Haskell.
We will look at the <code class="language-plaintext highlighter-rouge">PlistBuddy</code> OSX UNIX command, its
internal shell, and how to provide access to this shell for Haskell
users.</p>
<!--MORE-->
<h3 id="plists-and-plistbuddy">Plists and PlistBuddy</h3>
<p>Plists are a format used in OSX and iOS for storing configuration data,
and are sometimes used as small, text-based databases.
They are, to a first approximation, XML-based representations of JSON-style records,
where each Plist is a single file with one, sometimes large, XML structure inside.
OSX and iOS applications use libraries for reading, modifying and writing Plists.</p>
<p>PlistBuddy is a UNIX command for modifying Plists. PlistBuddy has a interactive
mode, and it is this interactive mode we are interested in reflecting into Haskell.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ /usr/libexec/PlistBuddy --help
Command Format:
Help - Prints this information
Exit - Exits the program, changes are not saved to the file
Save - Saves the current changes to the file
Revert - Reloads the last saved version of the file
Clear [<Type>] - Clears out all existing entries, and creates root of Type
Print [<Entry>] - Prints value of Entry. Otherwise, prints file
Set <Entry> <Value> - Sets the value at Entry to Value
Add <Entry> <Type> [<Value>] - Adds Entry to the plist, with value Value
Delete <Entry> - Deletes Entry from the plist
...
</code></pre></div></div>
<p>To give an example, we can create a Plist, populate it, examine it, and save it.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ /usr/libexec/PlistBuddy example.plist
File Doesn't Exist, Will Create: example.plist
Command: add user string 'Fred Flintstone'
Command: add age integer 39
Command: print
Dict {
age = 39
user = Fred Flintstone
}
Command: print age
39
Command: save
Saving...
Command: quit
</code></pre></div></div>
<p>The file <code class="language-plaintext highlighter-rouge">example.plist</code> is now</p>
<figure class="highlight"><pre><code class="language-xml" data-lang="xml"><span class="cp"><?xml version="1.0" encoding="UTF-8"?></span>
<span class="cp"><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "https://www.apple.com/DTDs/PropertyList-1.0.dtd"></span>
<span class="nt"><plist</span> <span class="na">version=</span><span class="s">"1.0"</span><span class="nt">></span>
<span class="nt"><dict></span>
<span class="nt"><key></span>age<span class="nt"></key></span>
<span class="nt"><integer></span>39<span class="nt"></integer></span>
<span class="nt"><key></span>user<span class="nt"></key></span>
<span class="nt"><string></span>Fred Flintstone<span class="nt"></string></span>
<span class="nt"></dict></span>
<span class="nt"></plist></span></code></pre></figure>
<p>This is a trivial example; dictionaries and arrays can be nested to arbitrary depths.
<code class="language-plaintext highlighter-rouge">PlistBuddy</code> robustly looks after the reading and writing of the XML file.
Of course, we could write a <code class="language-plaintext highlighter-rouge">PlistBuddy</code>-clone in Haskell, but this misses the point:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">PlistBuddy</code> supports 2 additional file formats used by OSX & iOS, including a binary format.</li>
<li><code class="language-plaintext highlighter-rouge">PlistBuddy</code> supports multi-megabyte plists out of the box.</li>
</ul>
<p>By providing a useable API for Haskell users that uses <code class="language-plaintext highlighter-rouge">PlistBuddy</code> directly, we quickly can use
the capability in a robust way. If in the future, plist resources ends up on the critical path,
we can replace the library with a native Haskell library, or use the FFI to call a C library.</p>
<h3 id="the-plistbuddy-remote-monad">The PlistBuddy Remote Monad</h3>
<p>Remote monad calls are a generalization of remote procedure calls. The
calls do not need to be across a network; the “remote” in remote monad
designates externalization from the Haskell runtime system. In this case,
we are sending a text command to another UNIX process, and receiving text
back.</p>
<p>Our basic API consists of (1) an opener, to open a channel to the remote
resource; (2) the <code class="language-plaintext highlighter-rouge">send</code> command; and (3) the remote monadic commands,
which use a monad call <code class="language-plaintext highlighter-rouge">PlistBuddy</code>.</p>
<figure class="highlight"><pre><code class="language-haskell" data-lang="haskell"><span class="c1">-- Our opener</span>
<span class="n">openPlist</span> <span class="o">::</span> <span class="kt">FilePath</span> <span class="o">-></span> <span class="kt">IO</span> <span class="kt">Plist</span>
<span class="c1">-- Our 'send' command</span>
<span class="n">send</span> <span class="o">::</span> <span class="kt">Plist</span> <span class="o">-></span> <span class="kt">PlistBuddy</span> <span class="n">a</span> <span class="o">-></span> <span class="kt">IO</span> <span class="n">a</span>
<span class="c1">-- Our remote monadic commands</span>
<span class="n">help</span> <span class="o">::</span> <span class="kt">PlistBuddy</span> <span class="kt">Text</span>
<span class="n">exit</span> <span class="o">::</span> <span class="kt">PlistBuddy</span> <span class="nb">()</span>
<span class="n">save</span> <span class="o">::</span> <span class="kt">PlistBuddy</span> <span class="nb">()</span>
<span class="n">revert</span> <span class="o">::</span> <span class="kt">PlistBuddy</span> <span class="nb">()</span>
<span class="n">clear</span> <span class="o">::</span> <span class="kt">Value</span> <span class="o">-></span> <span class="kt">PlistBuddy</span> <span class="nb">()</span>
<span class="n">get</span> <span class="o">::</span> <span class="p">[</span><span class="kt">Text</span><span class="p">]</span> <span class="o">-></span> <span class="kt">PlistBuddy</span> <span class="kt">Value</span>
<span class="n">set</span> <span class="o">::</span> <span class="p">[</span><span class="kt">Text</span><span class="p">]</span> <span class="o">-></span> <span class="kt">Value</span> <span class="o">-></span> <span class="kt">PlistBuddy</span> <span class="nb">()</span>
<span class="n">add</span> <span class="o">::</span> <span class="p">[</span><span class="kt">Text</span><span class="p">]</span> <span class="o">-></span> <span class="kt">Value</span> <span class="o">-></span> <span class="kt">PlistBuddy</span> <span class="nb">()</span>
<span class="n">delete</span> <span class="o">::</span> <span class="p">[</span><span class="kt">Text</span><span class="p">]</span> <span class="o">-></span> <span class="kt">PlistBuddy</span> <span class="nb">()</span>
<span class="kr">data</span> <span class="kt">Value</span> <span class="o">=</span> <span class="kt">String</span> <span class="kt">Text</span>
<span class="o">|</span> <span class="kt">Array</span> <span class="p">[</span><span class="kt">Value</span><span class="p">]</span>
<span class="o">|</span> <span class="kt">Dict</span> <span class="p">[(</span><span class="kt">Text</span><span class="p">,</span><span class="kt">Value</span><span class="p">)]</span>
<span class="o">|</span> <span class="kt">Bool</span> <span class="kt">Bool</span>
<span class="o">|</span> <span class="kt">Real</span> <span class="kt">Double</span>
<span class="o">|</span> <span class="kt">Integer</span> <span class="kt">Integer</span>
<span class="o">|</span> <span class="kt">Date</span> <span class="kt">UTCTime</span>
<span class="o">|</span> <span class="kt">Data</span> <span class="kt">ByteString</span></code></pre></figure>
<p>The <code class="language-plaintext highlighter-rouge">openPlist</code> command opens a plist file by spawning off a PlistBuddy instance,
using the <a href="https://hackage.Haskell.org/package/posix-pty">posix-pty</a> package,
which creates a pseudo terminal for the UNIX process. The <code class="language-plaintext highlighter-rouge">send</code> command sends a monadic <code class="language-plaintext highlighter-rouge">PlistBuddy</code>
command to a specific Plist. Finally, the commands are the primitives in
the remote monad called <code class="language-plaintext highlighter-rouge">PlistBuddy</code>. To repeat the earlier example:</p>
<figure class="highlight"><pre><code class="language-haskell" data-lang="haskell"><span class="kt">GHCi</span><span class="o">></span> <span class="n">p</span> <span class="o"><-</span> <span class="n">openPlist</span> <span class="s">"example.plist"</span>
<span class="kt">GHCi</span><span class="o">></span> <span class="n">send</span> <span class="n">p</span> <span class="o">$</span> <span class="kr">do</span> <span class="p">{</span> <span class="n">add</span> <span class="p">[</span><span class="s">"user"</span><span class="p">]</span> <span class="p">(</span><span class="kt">String</span> <span class="s">"Fred Flintstone"</span><span class="p">);</span> <span class="n">add</span> <span class="p">[</span><span class="s">"age"</span><span class="p">]</span> <span class="p">(</span><span class="kt">Integer</span> <span class="mi">39</span><span class="p">)</span> <span class="p">}</span>
<span class="kt">GHCi</span><span class="o">></span> <span class="n">send</span> <span class="n">p</span> <span class="o">$</span> <span class="n">get</span> <span class="kt">[]</span>
<span class="kt">Dict</span> <span class="p">[(</span><span class="s">"age"</span><span class="p">,</span><span class="kt">Integer</span> <span class="mi">39</span><span class="p">),(</span><span class="s">"user"</span><span class="p">,</span><span class="kt">String</span> <span class="s">"Fred Flintstone"</span><span class="p">)]</span>
<span class="kt">GHCi</span><span class="o">></span> <span class="n">send</span> <span class="n">p</span> <span class="o">$</span> <span class="n">get</span> <span class="p">[</span><span class="s">"age"</span><span class="p">]</span>
<span class="kt">Integer</span> <span class="mi">39</span>
<span class="kt">GHCi</span><span class="o">></span> <span class="n">send</span> <span class="n">p</span> <span class="o">$</span> <span class="kr">do</span> <span class="p">{</span> <span class="n">save</span> <span class="p">;</span> <span class="n">exit</span> <span class="p">}</span></code></pre></figure>
<p>We can now programmatically build <code class="language-plaintext highlighter-rouge">PlistBuddy</code> monadic functions that manipulate
plists. Further, because we use a <code class="language-plaintext highlighter-rouge">send</code> function, we can manipulate many plists
at the same time.</p>
<h3 id="implementing-the-plistbuddy-remote-monad">Implementing the PlistBuddy Remote Monad</h3>
<p>We have <a href="https://github.com/andygill/plist-buddy">implemented</a>
this design in the package
<a href="https://hackage.haskell.org/package/plist-buddy">plist-buddy</a>.
There are many ways of implementing a remote monad. In this case,
<code class="language-plaintext highlighter-rouge">PlistBuddy</code> is our remote monad, constructed out of a reader monad for the type <code class="language-plaintext highlighter-rouge">Plist</code>,
and an exception monad for Plist-specific exceptions.</p>
<figure class="highlight"><pre><code class="language-haskell" data-lang="haskell"><span class="kr">newtype</span> <span class="kt">PlistBuddy</span> <span class="n">a</span> <span class="o">=</span> <span class="kt">PlistBuddy</span> <span class="p">(</span><span class="kt">ExceptT</span> <span class="kt">PlistError</span> <span class="p">(</span><span class="kt">ReaderT</span> <span class="kt">Plist</span> <span class="kt">IO</span><span class="p">)</span> <span class="n">a</span><span class="p">)</span>
<span class="kr">deriving</span> <span class="p">(</span><span class="kt">Functor</span><span class="p">,</span> <span class="kt">Applicative</span><span class="p">,</span> <span class="kt">Monad</span><span class="p">,</span> <span class="kt">MonadError</span> <span class="kt">PlistError</span><span class="p">,</span> <span class="kt">MonadReader</span> <span class="kt">Plist</span><span class="p">,</span> <span class="kt">MonadIO</span><span class="p">)</span>
<span class="kr">newtype</span> <span class="kt">PlistError</span> <span class="o">=</span> <span class="kt">PlistError</span> <span class="kt">String</span>
<span class="kr">deriving</span> <span class="p">(</span><span class="kt">Show</span><span class="p">,</span> <span class="kt">Eq</span><span class="p">)</span></code></pre></figure>
<p>The <code class="language-plaintext highlighter-rouge">openPlist</code> command spawns the /usr/libexec/PlistBuddy shell command, and returns the handles to this
process.</p>
<figure class="highlight"><pre><code class="language-haskell" data-lang="haskell"><span class="n">openPlist</span> <span class="o">::</span> <span class="kt">FilePath</span> <span class="o">-></span> <span class="kt">IO</span> <span class="kt">Plist</span>
<span class="n">openPlist</span> <span class="n">fileName</span> <span class="o">=</span> <span class="kr">do</span>
<span class="p">(</span><span class="n">pty</span><span class="p">,</span><span class="n">ph</span><span class="p">)</span> <span class="o"><-</span> <span class="n">spawnWithPty</span>
<span class="o">...</span>
<span class="s">"/usr/libexec/PlistBuddy"</span>
<span class="p">[</span><span class="s">"-x"</span><span class="p">,</span><span class="n">fileName</span><span class="p">]</span>
<span class="o">...</span>
<span class="o">...</span>
<span class="n">return</span> <span class="o">$</span> <span class="kt">Plist</span> <span class="n">pty</span> <span class="n">ph</span> <span class="o">...</span></code></pre></figure>
<p>The <code class="language-plaintext highlighter-rouge">send</code> command sends the <code class="language-plaintext highlighter-rouge">PlistBuddy</code> to the plist by executing the inner monad,
using the standard monad transformer run functions. Slightly simplified, we have:</p>
<figure class="highlight"><pre><code class="language-haskell" data-lang="haskell"><span class="n">send</span> <span class="o">::</span> <span class="kt">Plist</span> <span class="o">-></span> <span class="kt">PlistBuddy</span> <span class="n">a</span> <span class="o">-></span> <span class="kt">IO</span> <span class="n">a</span>
<span class="n">send</span> <span class="n">dev</span> <span class="p">(</span><span class="kt">PlistBuddy</span> <span class="n">m</span><span class="p">)</span> <span class="o">=</span> <span class="kr">do</span>
<span class="n">v</span> <span class="o"><-</span> <span class="n">runReaderT</span> <span class="p">(</span><span class="n">runExceptT</span> <span class="n">m</span><span class="p">)</span> <span class="n">dev</span>
<span class="kr">case</span> <span class="n">v</span> <span class="kr">of</span>
<span class="kt">Left</span> <span class="p">(</span><span class="kt">PlistError</span> <span class="n">msg</span><span class="p">)</span> <span class="o">-></span> <span class="n">fail</span> <span class="n">msg</span>
<span class="kt">Right</span> <span class="n">val</span> <span class="o">-></span> <span class="n">return</span> <span class="n">val</span></code></pre></figure>
<p>Finally, each <code class="language-plaintext highlighter-rouge">PlistBuddy</code> command calls the plist process using an internal utility function <code class="language-plaintext highlighter-rouge">command</code>.
To take an example, consider <code class="language-plaintext highlighter-rouge">delete</code>.</p>
<figure class="highlight"><pre><code class="language-haskell" data-lang="haskell"><span class="n">command</span> <span class="o">::</span> <span class="kt">Plist</span> <span class="o">-></span> <span class="kt">ByteString</span> <span class="o">-></span> <span class="kt">IO</span> <span class="kt">ByteString</span>
<span class="n">command</span> <span class="n">plist</span> <span class="n">input</span> <span class="o">=</span> <span class="kr">do</span>
<span class="n">writePty</span> <span class="n">pty</span> <span class="p">(</span><span class="n">input</span> <span class="o"><></span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">)</span>
<span class="n">recvReply</span> <span class="n">pty</span>
<span class="kr">where</span>
<span class="n">pty</span> <span class="o">=</span> <span class="n">plist_pty</span> <span class="n">plist</span>
<span class="n">delete</span> <span class="o">::</span> <span class="p">[</span><span class="kt">Text</span><span class="p">]</span> <span class="o">-></span> <span class="kt">PlistBuddy</span> <span class="nb">()</span>
<span class="n">delete</span> <span class="n">entry</span> <span class="o">=</span> <span class="kr">do</span>
<span class="n">plist</span> <span class="o"><-</span> <span class="n">ask</span>
<span class="n">res</span> <span class="o"><-</span> <span class="n">liftIO</span> <span class="o">$</span> <span class="n">command</span> <span class="n">plist</span> <span class="o">$</span> <span class="s">"delete "</span> <span class="o"><></span> <span class="kt">BS</span><span class="o">.</span><span class="n">concat</span> <span class="p">[</span> <span class="s">":"</span> <span class="o"><></span> <span class="n">quoteText</span> <span class="n">e</span> <span class="o">|</span> <span class="n">e</span> <span class="o"><-</span> <span class="n">entry</span> <span class="p">]</span>
<span class="kr">case</span> <span class="n">res</span> <span class="kr">of</span>
<span class="s">""</span> <span class="o">-></span> <span class="n">return</span> <span class="nb">()</span>
<span class="kr">_</span> <span class="o">-></span> <span class="n">throwPlistError</span> <span class="o">$</span> <span class="kt">PlistError</span> <span class="o">$</span> <span class="s">"delete failed: "</span> <span class="o">++</span> <span class="n">show</span> <span class="n">res</span></code></pre></figure>
<p><code class="language-plaintext highlighter-rouge">command</code> is our remote evaluator, which uses the external process shell; sending
commands, and receiving replies.
Each remote monad primitive, like <code class="language-plaintext highlighter-rouge">delete</code>, uses <code class="language-plaintext highlighter-rouge">command</code> to send a textual command to
the plist buddy sub-process, and waits for a reply, in this case the empty string.
We call this implementation a <strong>weak remote monad</strong>, because each remote monad primitive
directly invokes the remote service. There is a one-to-one correspondence between primitive and
remote invocation, and we make no attempt to bundle commands into packets or optimize
this communication.
To summarize, the <code class="language-plaintext highlighter-rouge">PlistBuddy</code> remote monad is a typed API around the <code class="language-plaintext highlighter-rouge">command</code> utility function
for talking to an external shell. The design pattern guided our external API, and helped us
structure our internal implementation.</p>
<p>In the next blog article, we will look at a <em>strong</em> remote monad, where we bundle
together many commands into a single packet, to amortize the cost of using a remote
service. For anyone that wants to know more, we also have a web page about the
<a href="/practice/remotemonad/">remote monad</a>, including a list
of existing applications that use the remote monad, and a link to our
Haskell Symposium paper.</p>
Thu, 10 Dec 2015 00:00:00 +0000
https://www.ittc.ku.edu/csdl/fpg/2015/12/10/remote-monad-part-1/
https://www.ittc.ku.edu/csdl/fpg/2015/12/10/remote-monad-part-1/
-
2 New KU FPG Papers
<p>Two papers from the University of Kansas Functional Programming Group have been accepted for publication at Haskell’15! One about using
HERMIT for equational reasoning, and the other about a monad-based design pattern for remote control that externalizes monadic
execution. We’ve put the preprints on our webpage.</p>
<!--MORE-->
<h2 id="reasoning-with-the-hermit-tool-support-for-equational-reasoning-on-ghc-core-programs">Reasoning with the HERMIT: Tool support for equational reasoning on GHC core programs</h2>
<ul class="citation-list">
<li><p>A. Farmer, N. Sculthorpe, and A. Gill, “<a href="/papers/Farmer-15-HERMIT-reasoning">Reasoning with the
<span>HERMIT</span>: <span>T</span>ool support for equational reasoning
on <span>GHC</span> <span>C</span>ore programs</a>,” in <span><em>Proceedings
of the 8th ACM SIGPLAN Symposium on Haskell</em></span>, Haskell 2015, (New
York, NY, USA), pp. 23–34, ACM, 2015.</p>
</li>
</ul>
<p>A benefit of pure functional programming is that it encourages equational reasoning. However, the Haskell language has lacked direct
tool support for such reasoning. Consequently, reasoning about Haskell programs is either performed manually, or in another language
that does provide tool support (e.g. Agda or Coq). HERMIT is a Haskell-specific toolkit designed to support equational reasoning and
user-guided program transformation, and to do so as part of the GHC compilation pipeline. This paper describes HERMIT’s recently
developed support for equational reasoning, and presents two case studies of HERMIT usage: checking that type-class laws hold for
specific instance declarations, and mechanising textbook equational reasoning.</p>
<h2 id="the-remote-monad-design-pattern">The remote monad design pattern</h2>
<ul class="citation-list">
<li><p>A. Gill, N. Sculthorpe, J. Dawson, A. Eskilson, A. Farmer, M. Grebe,
J. Rosenbluth, R. Scott, and J. Stanton, “<a href="/papers/Gill-15-RemoteMonad">The remote monad design
pattern</a>,” in <span><em>Proceedings of the 8th ACM SIGPLAN Symposium on
Haskell</em></span>, (New York, NY, USA), pp. 59–70, ACM, 2015.</p>
</li>
</ul>
<p>Remote Procedure Calls are expensive. This paper demonstrates how to reduce the cost of calling remote procedures from Haskell by using
the remote monad design pattern, which amortizes the cost of remote calls. This gives the Haskell community access to remote
capabilities that are not directly supported, at a surprisingly inexpensive cost.</p>
<p>We explore the remote monad design pattern through six models of remote execution patterns, using a simulated Internet of Things
toaster as a running example. We consider the expressiveness and optimizations enabled by each remote execution model, and assess the
feasibility of our approach. We then present a full-scale case study: a Haskell library that provides a Foreign Function Interface to
the JavaScript Canvas API. Finally, we discuss existing instances of the remote monad design pattern found in Haskell libraries.</p>
<ul>
<li>Enjoy!</li>
</ul>
<p>KU@FPG</p>
Sun, 19 Jul 2015 00:00:00 +0000
https://www.ittc.ku.edu/csdl/fpg/2015/07/19/new-papers/
https://www.ittc.ku.edu/csdl/fpg/2015/07/19/new-papers/