<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">

<channel>
<title>Well-Typed Blog</title>
<atom:link href="https://well-typed.com/blog/rss2.xml" rel="self" type="application/rss+xml" />
<link>https://well-typed.com/blog/</link>
<description>Because Well-Typed Programs Don't Go Wrong</description>
<language>en</language>
<pubDate>Thu, 08 Oct 2026 00:00:00 GMT</pubDate>
<lastBuildDate>Thu, 08 Oct 2026 00:00:00 GMT</lastBuildDate>
<generator>Well-Typed Blog Generator</generator>
<image>
    <url>https://well-typed.com/img/wtnlogoplain.svg</url>
    <title>Well-Typed Blog</title>
    <link>https://well-typed.com/blog/</link>
    <description>Because Well-Typed Programs Don't Go Wrong</description>
</image>
<item>
    <title>First official release of hs-bindgen</title>
    <link>https://well-typed.com/blog/2026/10/hs-bindgen-official-release</link>
    <description><![CDATA[Well-Typed and the entire hs-bindgen team---there are quite a few of us, see
below---are delighted to announce hs-bindgen 1.0, the first official release.
In case you missed the announcement of the alpha release,
hs-bindgen is a tool to automatically generate Haskell bindings from C
headers.

In this [...]]]></description>
    <dc:creator>edsko</dc:creator>
    <category>hs-bindgen</category>
    <category>open-source</category>
    <category>foreign function interface</category>
    <guid isPermaLink="false">http://www.well-typed.com/blog/2026/10/hs-bindgen-official-release</guid>
    <pubDate>Thu, 08 Oct 2026 00:00:00 GMT</pubDate>
    <content:encoded><![CDATA[<p>Well-Typed and the entire <code>hs-bindgen</code> team—there are quite a few of us, see
below—are delighted to announce <code>hs-bindgen</code> 1.0, the first official release.
In case you missed the <a href="https://well-typed.com/blog/2026/02/hs-bindgen-alpha/">announcement of the alpha release</a>,
<code>hs-bindgen</code> is a tool to automatically generate Haskell bindings from C
headers.</p>
<p>In this blog post we will start with a <em>very</em> brief intro to <code>hs-bindgen</code>, but
we will not repeat everything that we discussed in the first two blog posts that
announced the <a href="https://well-typed.com/blog/2026/02/hs-bindgen-alpha/">first</a> and <a href="https://well-typed.com/blog/2026/03/hs-bindgen-alpha2/">second</a> alpha releases.
Instead, we will highlight a few of the major changes; for a full list (which is
quite long!), please see the <a href="https://hackage.haskell.org/package/hs-bindgen-1.0.0.0/changelog">changelog</a>.</p>
<!-- more -->
<p>You can <a href="https://hackage.haskell.org/package/hs-bindgen">find <code>hs-bindgen</code> on Hackage</a>.</p>
<h2 id="introduction">Introduction</h2>
<p>Consider this small C header:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="pp">#include </span><span class="im">&lt;stddef.h&gt;</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="kw">typedef</span> <span class="dt">size_t</span> length<span class="op">;</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a><span class="kw">typedef</span> <span class="kw">struct</span> str <span class="op">{</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a>  <span class="dt">char</span><span class="op">*</span> bytes<span class="op">;</span></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a>  length len<span class="op">;</span></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a><span class="op">}</span> str<span class="op">;</span></span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a><span class="co">/**</span></span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a><span class="co"> * Construct </span><span class="an">\ref</span><span class="co"> </span><span class="cv">str</span><span class="co"> from null-terminated input</span></span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a><span class="co"> */</span></span>
<span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a>str new_string<span class="op">(</span><span class="dt">const</span> <span class="dt">char</span><span class="op">*</span> str<span class="op">);</span></span></code></pre></div>
<p>When we run <code>hs-bindgen</code> on this example</p>
<pre><code>$ hs-bindgen-cli preprocess example.h --omit-field-prefixes # .. more args</code></pre>
<p>it will generate a number of Haskell modules, which we summarize below:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="kw">newtype</span> <span class="dt">Length</span> <span class="ot">=</span> <span class="dt">Length</span>{</span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a><span class="ot">    unwrap ::</span> <span class="dt">CSize</span></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a>  }</span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">Str</span> <span class="ot">=</span> <span class="dt">Str</span>{</span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a><span class="ot">    bytes ::</span> <span class="dt">Ptr</span> <span class="dt">CChar</span></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a>  ,<span class="ot"> len   ::</span> <span class="dt">Length</span></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a>  }</span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a><span class="co">-- .. bunch of instances ..</span></span>
<span id="cb3-11"><a href="#cb3-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-12"><a href="#cb3-12" aria-hidden="true" tabindex="-1"></a><span class="co">{-| Construct 'Str' from null-terminated input</span></span>
<span id="cb3-13"><a href="#cb3-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-14"><a href="#cb3-14" aria-hidden="true" tabindex="-1"></a><span class="co">    __C declaration:__ @new_string@</span></span>
<span id="cb3-15"><a href="#cb3-15" aria-hidden="true" tabindex="-1"></a><span class="co">    .. other Haddocks elided, here and elsewhere ..</span></span>
<span id="cb3-16"><a href="#cb3-16" aria-hidden="true" tabindex="-1"></a><span class="co">-}</span></span>
<span id="cb3-17"><a href="#cb3-17" aria-hidden="true" tabindex="-1"></a><span class="ot">new_string ::</span></span>
<span id="cb3-18"><a href="#cb3-18" aria-hidden="true" tabindex="-1"></a>     <span class="dt">PtrConst</span> <span class="dt">CChar</span> <span class="co">-- ^ __C declaration:__ @str@</span></span>
<span id="cb3-19"><a href="#cb3-19" aria-hidden="true" tabindex="-1"></a>  <span class="ot">-&gt;</span> <span class="dt">IO</span> <span class="dt">Str</span></span>
<span id="cb3-20"><a href="#cb3-20" aria-hidden="true" tabindex="-1"></a>new_string <span class="ot">=</span> <span class="co">{- .. -}</span></span></code></pre></div>
<p>Some notable features:</p>
<ul>
<li>C names are translated into Haskell names, modifying them to match Haskell’s
naming rules where needed.</li>
<li>With <code>--omit-field-prefixes</code> the Haskell code relies on
<code>DuplicateRecordFields</code>; without that option it would have prefixed every
field (<code>lengthUnwrap</code>, <code>strBytes</code>, etc.).</li>
<li>No Haskell definition is generated for <code>size_t</code>, defined in <code>stddef.h</code>;
instead, it is mapped to the existing <a href="https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Foreign-C-Types.html#t:CSize"><code>CSize</code></a> type by means of
a <em>binding specification</em> for the standard C library. Binding specifications
make binding generation compositional: bindings generated for one library can
be reused in another.</li>
<li>The <code>length</code> typedef is translated to a Haskell newtype, but the <code>str</code> typedef
around the <code>str</code> struct is not: the latter exists in C only for syntactic
convenience.</li>
<li>Function <code>new_string</code> returns a struct by value, which is not natively
supported by Haskell’s FFI, and so <code>hs-bindgen</code> will also generate the
necessary C wrappers.</li>
<li>All Haskell declarations get Haddock comments linking them to the
corresponding C declaration; if that C declaration has associated Doxygen
comments, those are also translated.</li>
</ul>
<p>In the remainder of this blog post we will highlight some of the improvements
we’ve made since the alpha release. As you will see, there are a <em>lot</em> of
details to get right!</p>
<h2 id="ffi-types">FFI types</h2>
<p>Consider a C function</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> foo<span class="op">(</span>MyType x<span class="op">)</span></span></code></pre></div>
<p>The obvious translation into Haskell is</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a>foreign <span class="kw">import</span> <span class="co">{- .. -}</span> foo_wrapper :: <span class="dt">MyType</span> -&gt; <span class="dt">IO</span> ()</span></code></pre></div>
<p>However, this is valid only if GHC can see that <code>MyType</code> is a type supported
by the Haskell FFI; in particular, this means that GHC must be able to unwrap
that <code>MyType</code> newtype until it gets a base type from a fixed set of permitted
foreign types.</p>
<p>This is hard to ensure in general, especially if <code>MyType</code> is not newly
generated, but comes from another library (previously generated by <code>hs-bindgen</code>,
or handwritten). In the alpha release we resolved this by reducing every
argument to a base type, but this resulted in code that was less portable than
it could have been; for example, we might use <code>Int32</code> instead of <code>MyType</code>.</p>
<p>We now solve this in a better way: given a Haskell type corresponding to a C
type, a binding specification can (and should) specify an <a href="https://github.com/well-typed/hs-bindgen/blob/main/manual/low-level/translation/ffi-types.md">“FFI
type”</a> for that Haskell type: an FFI type is a pair of a module
<code>M</code> and type <code>F</code> such that <code>F</code> is legal to appear in a <code>foreign import</code> in a
context where <code>M</code> is in scope. For example, <code>Foreign.C.CSize</code> is an FFI type,
because <code>CSize</code> can appear in a <code>foreign import</code> if <code>Foreign.C</code> is imported:
<code>Foreign.C</code> exports all the necessary newtype constructors.</p>
<p>The translation from <code>MyType</code> to its FFI type (which might be <code>MyType</code> itself)
is now also more general; <code>MyType</code> just needs to have an instance of the
<code>HasFFIType</code> type class:</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> <span class="dt">HasFFIType</span> a <span class="kw">where</span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a>  <span class="kw">type</span> <span class="dt">FFIType</span><span class="ot"> a ::</span> <span class="dt">Type</span></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a><span class="ot">  toFFIType   ::</span> a <span class="ot">-&gt;</span> <span class="dt">FFIType</span> a</span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a><span class="ot">  fromFFIType ::</span> <span class="dt">FFIType</span> a <span class="ot">-&gt;</span> a</span></code></pre></div>
<p>The <code>FFIType</code> type instance should line up with the FFI type specified in the
binding spec. In many cases, <code>toFFIType</code> and <code>fromFFIType</code> can simply be
<code>coerce</code>.</p>
<p>As an example, if <code>MyType</code>’s FFI type is <code>CSize</code>, then <code>hs-bindgen</code> will
generate code</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a>foreign <span class="kw">import</span> <span class="co">{- .. -}</span> foo_wrapper :: <span class="dt">CSize</span> -&gt; <span class="dt">IO</span> ()</span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a><span class="ot">foo ::</span> <span class="dt">MyType</span> <span class="ot">-&gt;</span> <span class="dt">IO</span> ()</span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a>foo x <span class="ot">=</span> foo_wrapper (toFFIType x)</span></code></pre></div>
<p>In general the bindings generated by <code>hs-bindgen</code> are still specific to the
specified target platform (the host, by default), but the introduction of
FFI types reduces an avoidable source of non-portability.</p>
<h2 id="unnamed-declarations">Unnamed declarations</h2>
<p>In Haskell, we might define a binary tree with values in the leaves as</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">BTree</span> a <span class="ot">=</span> <span class="dt">Leaf</span> a <span class="op">|</span> <span class="dt">Branch</span> (<span class="dt">BTree</span> a) (<span class="dt">BTree</span> a)</span></code></pre></div>
<p>An analogous definition in C might look something like this:</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="kw">enum</span> tag <span class="op">{</span>LEAF<span class="op">,</span> BRANCH<span class="op">};</span></span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> BTree <span class="op">{</span></span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a>  <span class="kw">enum</span> tag constr<span class="op">;</span></span>
<span id="cb9-5"><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a>  <span class="kw">union</span> <span class="op">{</span></span>
<span id="cb9-6"><a href="#cb9-6" aria-hidden="true" tabindex="-1"></a>    <span class="dt">int</span> value<span class="op">;</span></span>
<span id="cb9-7"><a href="#cb9-7" aria-hidden="true" tabindex="-1"></a>    <span class="kw">struct</span> <span class="op">{</span></span>
<span id="cb9-8"><a href="#cb9-8" aria-hidden="true" tabindex="-1"></a>      <span class="kw">struct</span> BTree<span class="op">*</span> left<span class="op">;</span></span>
<span id="cb9-9"><a href="#cb9-9" aria-hidden="true" tabindex="-1"></a>      <span class="kw">struct</span> BTree<span class="op">*</span> right<span class="op">;</span></span>
<span id="cb9-10"><a href="#cb9-10" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span> branch<span class="op">;</span></span>
<span id="cb9-11"><a href="#cb9-11" aria-hidden="true" tabindex="-1"></a>  <span class="op">};</span></span>
<span id="cb9-12"><a href="#cb9-12" aria-hidden="true" tabindex="-1"></a><span class="op">};</span></span></code></pre></div>
<p>The <code>union</code> is <a href="https://gcc.gnu.org/onlinedocs/gcc/Unnamed-Fields.html">anonymous</a>: in C, its fields are accessed as if
they are fields of the parent (<code>struct BTree</code>). In addition, the inner <code>struct</code>
is unnamed, although the field of the union in this case <em>does</em> have a name
(<code>branch</code>).</p>
<p>Haskell does not have anonymous records or anonymous sums, and so we must choose
names. We have a policy in <code>hs-bindgen</code> to never generate numbered names
(“struct 1”), and instead pick names based on surrounding clues, to avoid the
generated code changing in unpredictable ways when the C headers change. In this
case, the union is named after its first field and its parent:
<code>BTree_anon'value</code>; similarly the struct is named after its parent and the name
of the field: <code>BTree_anon'value_branch</code>. The <code>anon'</code> infix avoids name clashes
with other generated names (C names cannot contain ticks).</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="kw">newtype</span> <span class="dt">Tag</span> <span class="ot">=</span> <span class="dt">Tag</span> <span class="dt">CUInt</span></span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a><span class="kw">pattern</span> <span class="dt">LEAF</span>, <span class="dt">BRANCH</span><span class="ot"> ::</span> <span class="dt">Tag</span></span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">BTree</span> <span class="ot">=</span> <span class="dt">BTree</span>{</span>
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true" tabindex="-1"></a><span class="ot">    constr     ::</span> <span class="dt">Tag</span></span>
<span id="cb10-6"><a href="#cb10-6" aria-hidden="true" tabindex="-1"></a>  ,<span class="ot"> anon'value ::</span> <span class="dt">BTree_anon'value</span></span>
<span id="cb10-7"><a href="#cb10-7" aria-hidden="true" tabindex="-1"></a>  }</span>
<span id="cb10-8"><a href="#cb10-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-9"><a href="#cb10-9" aria-hidden="true" tabindex="-1"></a><span class="kw">newtype</span> <span class="dt">BTree_anon'value</span> <span class="ot">=</span> <span class="op">..</span></span>
<span id="cb10-10"><a href="#cb10-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-11"><a href="#cb10-11" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">HasField</span> <span class="st">&quot;value&quot;</span>  <span class="dt">BTree</span> <span class="dt">CInt</span></span>
<span id="cb10-12"><a href="#cb10-12" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">HasField</span> <span class="st">&quot;branch&quot;</span> <span class="dt">BTree</span> <span class="dt">BTree_anon'value_branch</span></span>
<span id="cb10-13"><a href="#cb10-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-14"><a href="#cb10-14" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">HasField</span> <span class="st">&quot;value&quot;</span>  (<span class="dt">Ptr</span> <span class="dt">BTree</span>) (<span class="dt">Ptr</span> <span class="dt">CInt</span>)</span>
<span id="cb10-15"><a href="#cb10-15" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">HasField</span> <span class="st">&quot;branch&quot;</span> (<span class="dt">Ptr</span> <span class="dt">BTree</span>) (<span class="dt">Ptr</span> <span class="dt">BTree_anon'value_branch</span>)</span>
<span id="cb10-16"><a href="#cb10-16" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-17"><a href="#cb10-17" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">BTree_anon'value_branch</span> <span class="ot">=</span> <span class="dt">BTree_anon'value_branch</span>{</span>
<span id="cb10-18"><a href="#cb10-18" aria-hidden="true" tabindex="-1"></a><span class="ot">    left  ::</span> <span class="dt">Ptr</span> <span class="dt">BTree</span></span>
<span id="cb10-19"><a href="#cb10-19" aria-hidden="true" tabindex="-1"></a>  ,<span class="ot"> right ::</span> <span class="dt">Ptr</span> <span class="dt">BTree</span></span>
<span id="cb10-20"><a href="#cb10-20" aria-hidden="true" tabindex="-1"></a>  }</span></code></pre></div>
<p>These names are perhaps a bit awkward, but in most cases code never has to deal
with them, because <code>hs-bindgen</code> generates “indirect” <code>HasField</code> instances that make
it possible to treat the fields of the union as if they are fields of the
parent, just like in C. Moreover, we have these instances also for <em>pointers</em>;
this means that when using the <code>OverloadedRecordDot</code> extension, we can write
Haskell code that processes these C values in a very natural way:<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a></p>
<div class="sourceCode" id="cb11"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a><span class="ot">toBTree ::</span> <span class="dt">Ptr</span> <span class="dt">C.BTree</span> <span class="ot">-&gt;</span> <span class="dt">IO</span> (<span class="dt">BTree</span> <span class="dt">CInt</span>)</span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a>toBTree bt <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a>    tag <span class="ot">&lt;-</span> peek bt<span class="op">.</span>constr</span>
<span id="cb11-4"><a href="#cb11-4" aria-hidden="true" tabindex="-1"></a>    <span class="kw">case</span> tag <span class="kw">of</span></span>
<span id="cb11-5"><a href="#cb11-5" aria-hidden="true" tabindex="-1"></a>      <span class="dt">C.LEAF</span> <span class="ot">-&gt;</span></span>
<span id="cb11-6"><a href="#cb11-6" aria-hidden="true" tabindex="-1"></a>        <span class="dt">Leaf</span> <span class="op">&lt;$&gt;</span> peek bt<span class="op">.</span>value</span>
<span id="cb11-7"><a href="#cb11-7" aria-hidden="true" tabindex="-1"></a>      <span class="dt">C.BRANCH</span> <span class="ot">-&gt;</span> <span class="kw">do</span></span>
<span id="cb11-8"><a href="#cb11-8" aria-hidden="true" tabindex="-1"></a>        left  <span class="ot">&lt;-</span> peek bt<span class="op">.</span>branch<span class="op">.</span>left</span>
<span id="cb11-9"><a href="#cb11-9" aria-hidden="true" tabindex="-1"></a>        right <span class="ot">&lt;-</span> peek bt<span class="op">.</span>branch<span class="op">.</span>right</span>
<span id="cb11-10"><a href="#cb11-10" aria-hidden="true" tabindex="-1"></a>        <span class="dt">Branch</span> <span class="op">&lt;$&gt;</span> toBTree left <span class="op">&lt;*&gt;</span> toBTree right</span></code></pre></div>
<h2 id="improved-record-dot-support">Improved record-dot support</h2>
<p>Although support in GHC for overloaded record <em>update</em> is sadly <a href="https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/overloaded_record_update.html">not yet as
polished</a> as overloaded record access, it can
nonetheless be quite helpful when dealing with C code that uses a lot of unions:
unions don’t have a natural representation in Haskell, and <code>hs-bindgen</code>
therefore encodes them as opaque datatypes.</p>
<p>To continue the example from the previous section, we can construct a value of
type <code>BTree</code> as</p>
<div class="sourceCode" id="cb12"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">HsBindgen.Runtime.Union</span> <span class="kw">qualified</span> <span class="kw">as</span> <span class="dt">Union</span></span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb12-3"><a href="#cb12-3" aria-hidden="true" tabindex="-1"></a><span class="ot">btree1 ::</span> <span class="dt">C.BTree</span></span>
<span id="cb12-4"><a href="#cb12-4" aria-hidden="true" tabindex="-1"></a>btree1 <span class="ot">=</span> <span class="dt">C.BTree</span>{</span>
<span id="cb12-5"><a href="#cb12-5" aria-hidden="true" tabindex="-1"></a>      constr     <span class="ot">=</span> <span class="dt">C.BRANCH</span></span>
<span id="cb12-6"><a href="#cb12-6" aria-hidden="true" tabindex="-1"></a>    , anon'value <span class="ot">=</span> Union.zero{</span>
<span id="cb12-7"><a href="#cb12-7" aria-hidden="true" tabindex="-1"></a>          branch <span class="ot">=</span> <span class="dt">C.BTree_anon'value_branch</span>{</span>
<span id="cb12-8"><a href="#cb12-8" aria-hidden="true" tabindex="-1"></a>              left  <span class="ot">=</span> nullPtr</span>
<span id="cb12-9"><a href="#cb12-9" aria-hidden="true" tabindex="-1"></a>            , right <span class="ot">=</span> nullPtr</span>
<span id="cb12-10"><a href="#cb12-10" aria-hidden="true" tabindex="-1"></a>            }</span>
<span id="cb12-11"><a href="#cb12-11" aria-hidden="true" tabindex="-1"></a>        }</span>
<span id="cb12-12"><a href="#cb12-12" aria-hidden="true" tabindex="-1"></a>    }</span></code></pre></div>
<p>If we take advantage of the indirect fields described in the previous section,
we can improve this further:</p>
<div class="sourceCode" id="cb13"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">HsBindgen.Runtime.Struct</span> <span class="kw">qualified</span> <span class="kw">as</span> <span class="dt">Struct</span></span>
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb13-3"><a href="#cb13-3" aria-hidden="true" tabindex="-1"></a><span class="ot">btree2 ::</span> <span class="dt">C.BTree</span></span>
<span id="cb13-4"><a href="#cb13-4" aria-hidden="true" tabindex="-1"></a>btree2 <span class="ot">=</span> Struct.zero{</span>
<span id="cb13-5"><a href="#cb13-5" aria-hidden="true" tabindex="-1"></a>     constr <span class="ot">=</span> <span class="dt">C.BRANCH</span></span>
<span id="cb13-6"><a href="#cb13-6" aria-hidden="true" tabindex="-1"></a>   , branch <span class="ot">=</span> Struct.zero{</span>
<span id="cb13-7"><a href="#cb13-7" aria-hidden="true" tabindex="-1"></a>         left  <span class="ot">=</span> nullPtr</span>
<span id="cb13-8"><a href="#cb13-8" aria-hidden="true" tabindex="-1"></a>       , right <span class="ot">=</span> nullPtr</span>
<span id="cb13-9"><a href="#cb13-9" aria-hidden="true" tabindex="-1"></a>       }</span>
<span id="cb13-10"><a href="#cb13-10" aria-hidden="true" tabindex="-1"></a>   }</span></code></pre></div>
<p>Note that <code>OverloadedRecordUpdate</code> currently also requires <code>RebindableSyntax</code>;
<code>hs-bindgen-runtime</code> provides a module <code>HsBindgen.Runtime.Overloading</code> that
restores the standard environment. See also <a href="https://github.com/well-typed/hs-bindgen/blob/main/manual/low-level/translation/record-dot-syntax.md#enabling-record-dot-syntax">Enabling record dot
syntax</a> in the manual.</p>
<h2 id="macros">Macros</h2>
<p>Preprocessor macros are used extensively in C development, and so for many
applications it is important they too get translated to Haskell. Macro handling
in <code>hs-bindgen</code> has been significantly improved since the alpha. Most of those
changes are internal, though they will become user-facing once we release
<a href="https://github.com/well-typed/hs-bindgen/issues/1003"><code>hs-bindgen</code>-as-a-library</a>, at which point it will also be
possible to substitute your own macro languages, replacing <code>hs-bindgen</code>’s
default <a href="https://hackage.haskell.org/package/c-expr-dsl"><code>c-expr</code></a>.</p>
<p>The full list of improvements can be found in the
<a href="https://hackage.haskell.org/package/hs-bindgen-1.0.0.0/changelog">changelog</a>; here we will mention just a few. First, we can
now parse declarations that use a mixture of macros we can parse and macros that
we cannot. For example, given</p>
<div class="sourceCode" id="cb14"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a><span class="pp">#define DeviceId </span><span class="dt">int</span></span>
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a><span class="pp">#define MUST_CHECK __attribute__</span><span class="op">((</span><span class="pp">warn_unused_result</span><span class="op">))</span></span>
<span id="cb14-3"><a href="#cb14-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb14-4"><a href="#cb14-4" aria-hidden="true" tabindex="-1"></a><span class="dt">int</span> upgradeFirmware<span class="op">(</span>DeviceId did<span class="op">)</span> MUST_CHECK<span class="op">;</span></span></code></pre></div>
<p>where the definition of <code>DeviceId</code> is parsed by <code>hs-bindgen</code> but the definition
of <code>MUST_CHECK</code> is not, we now generate</p>
<div class="sourceCode" id="cb15"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a><span class="kw">newtype</span> <span class="dt">DeviceId</span> <span class="ot">=</span> <span class="dt">DeviceId</span> <span class="dt">CInt</span></span>
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true" tabindex="-1"></a><span class="ot">upgradeFirmware ::</span> <span class="dt">DeviceId</span> <span class="ot">-&gt;</span> <span class="dt">IO</span> <span class="dt">CInt</span></span></code></pre></div>
<p>Second, detection of ambiguous macros (macros that do not have a unique
expansion throughout the translation unit) is improved. For example, given</p>
<div class="sourceCode" id="cb16"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a><span class="pp">#define A </span><span class="dv">5</span></span>
<span id="cb16-2"><a href="#cb16-2" aria-hidden="true" tabindex="-1"></a><span class="pp">#define B A</span></span>
<span id="cb16-3"><a href="#cb16-3" aria-hidden="true" tabindex="-1"></a><span class="pp">#define B A</span></span>
<span id="cb16-4"><a href="#cb16-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb16-5"><a href="#cb16-5" aria-hidden="true" tabindex="-1"></a><span class="pp">#define X </span><span class="dv">1</span></span>
<span id="cb16-6"><a href="#cb16-6" aria-hidden="true" tabindex="-1"></a><span class="pp">#define Y X</span></span>
<span id="cb16-7"><a href="#cb16-7" aria-hidden="true" tabindex="-1"></a><span class="pp">#undef X</span></span>
<span id="cb16-8"><a href="#cb16-8" aria-hidden="true" tabindex="-1"></a><span class="pp">#define X </span><span class="dv">2</span></span>
<span id="cb16-9"><a href="#cb16-9" aria-hidden="true" tabindex="-1"></a><span class="pp">#define Y X</span></span></code></pre></div>
<p>we generate bindings for <code>A</code> and <code>B</code></p>
<div class="sourceCode" id="cb17"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true" tabindex="-1"></a><span class="ot">a ::</span> <span class="dt">CInt</span></span>
<span id="cb17-2"><a href="#cb17-2" aria-hidden="true" tabindex="-1"></a>a <span class="ot">=</span> <span class="dv">5</span></span>
<span id="cb17-3"><a href="#cb17-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb17-4"><a href="#cb17-4" aria-hidden="true" tabindex="-1"></a><span class="ot">b ::</span> <span class="dt">CInt</span></span>
<span id="cb17-5"><a href="#cb17-5" aria-hidden="true" tabindex="-1"></a>b <span class="ot">=</span> a</span></code></pre></div>
<p>but not for <code>X</code> and <code>Y</code>.</p>
<p>Finally, macros can now be defined as part of the input to <code>hs-bindgen</code>
(<code>--hash-define FOO 1</code>). Previously such definitions had to be given twice, once
to <code>hs-bindgen</code> and once to the C compiler via <code>ghc-options: -optc-DFOO</code>, which
was easy to get wrong and could be hard to debug.</p>
<h2 id="conclusions">Conclusions</h2>
<p>Although Haskell’s foreign function interface is good, writing bindings to
large C libraries is nonetheless a laborious process. The “fire and forget”
approach that <code>hs-bindgen</code> offers takes care of a lot of the legwork. We’re
pretty proud of the fact that <code>hs-bindgen</code> can handle almost everything that
a C header might throw at it.</p>
<p>However, for many applications the bindings generated by <code>hs-bindgen</code> will be a
starting point only: they are a direct translation of the C code, and are
therefore very low-level. We are working on a
<a href="https://github.com/well-typed/binding-combinators"><code>binding-combinators</code></a> library that makes this
process easier, but ultimately such high-level bindings are then still written
by hand, albeit assisted by a library. The next big goal for <code>hs-bindgen</code> is
<em>automatic</em> generation of high-level bindings, guided by pluggable heuristics.</p>
<p>It has taken a lot of effort to get here, and we thank
<a href="https://www.anduril.com/">Anduril</a> for sponsoring this work. Within Well-Typed,
the <code>hs-bindgen</code> team includes or at one point included Armando Santos, Dominik
Schrempf, Edsko de Vries, Finley McIlwaine, Joris Dral, Matthias Heinzel, Oleg
Grenrus, Sam Derbyshire and Travis Cardwell. We have also benefited from
numerous contributions from external contributors in the form of issues and PRs,
for which we are grateful!</p>
<p><code>hs-bindgen</code> 1.0 is <a href="https://hackage.haskell.org/package/hs-bindgen">available on Hackage now</a>; give it a spin and <a href="https://github.com/well-typed/hs-bindgen/issues">let us
know</a> if you have any problems!</p>
<section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes">
<hr />
<ol>
<li id="fn1"><p>This assumes
a prescriptive binding spec to tell <code>hs-bindgen</code> to mark the tag as closed,
causing <code>hs-bindgen</code> to generate a <code>COMPLETE</code> pragma. See <a href="https://github.com/well-typed/hs-bindgen/issues/2101">#2101</a>.<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
</ol>
</section>
]]></content:encoded>
</item>
<item>
    <title>Memory profiling of large Haskell applications with ghc-debug</title>
    <link>https://well-typed.com/blog/2026/10/memory-profiling-large-haskell-applications-with-ghc-debug</link>
    <description><![CDATA[ghc-debug is a debugging tool for performing precise heap analysis of Haskell programs
(for more background, check out our previous post improving ghc-debug and the Case Study: Debugging a Haskell space leak).
While doing some memory profiling for Mercury, we took the opportunity to make some much
needed [...]]]></description>
    <dc:creator>hannes</dc:creator>
    <category>well-typed</category>
    <category>ghc</category>
    <category>ghc-debug</category>
    <category>community</category>
    <category>memory</category>
    <category>mercury</category>
    <category>profiling</category>
    <guid isPermaLink="false">http://www.well-typed.com/blog/2026/10/memory-profiling-large-haskell-applications-with-ghc-debug</guid>
    <pubDate>Fri, 02 Oct 2026 00:00:00 GMT</pubDate>
    <content:encoded><![CDATA[<p><a href="https://gitlab.haskell.org/ghc/ghc-debug"><code>ghc-debug</code></a> is a debugging tool for performing precise heap analysis of Haskell programs
(for more background, check out our <a href="https://well-typed.com/blog/2024/04/ghc-debug-improvements/">previous post improving <code>ghc-debug</code></a> and the <a href="https://well-typed.com/blog/2025/10/case-study-debugging-space-leak/">Case Study: Debugging a Haskell space leak</a>).
While doing some memory profiling for Mercury, we took the opportunity to make some much
needed improvements and quality of life fixes to both the <code>ghc-debug</code> set of libraries and the
<a href="https://hackage.haskell.org/package/ghc-debug-brick"><code>ghc-debug-brick</code></a> terminal user interface.</p>
<p>The 0.8 releases focused on improving responsiveness and usability on very large memory profiles.
In particular, <code>ghc-debug</code> now streams intermediate profile results, giving immediate feedback for huge heap traversals.</p>
<p>To summarise the improvements to <code>ghc-debug-brick</code>, it now:</p>
<ul>
<li>shows partial results, and allows heap traversals to be stopped at any moment without losing existing results;</li>
<li>implements history navigation, allowing you to switch between heap profile views; and</li>
<li>improves the decoding of stack frames and shows stack annotations.</li>
</ul>
<h2 id="streaming-of-intermediate-results">Streaming of intermediate results</h2>
<!-- more -->
<p>The <code>ghc-debug-brick</code> TUI is the starting point for any memory profiling with <code>ghc-debug</code>.
It provides useful heap traversal features, for example finding closures with certain properties (via an expressive filter system), summarising all live heap objects, or analysing thunk origins.</p>
<p>Most of these features need to traverse the full heap to provide a conclusive result.
A summary of all live heap objects cannot be complete until the full heap has been traversed.
This works fine for smaller heaps, but what if the program heap is really big? Say, more than half of system RAM?
The full heap traversal will take a long time, or even worse never finish, as it may use more memory than the system can provide.</p>
<p>To make <code>ghc-debug</code> usable with large heaps, we added support for streaming of results as they come in.
No more waiting around: seconds after you start the heap traversal, the first results are shown on the screen.
While you wait for more results, you can interact with the profile in a limited way.
If you think you’ve gathered enough data, you can stop the heap traversal by pressing <code>ESC</code> and then explore retainers and fields, or start a different heap traversal.</p>
<p>Note that a stopped traversal can’t be resumed, and you have to start from scratch to find more results.
There are currently no plans to add support for resuming a stopped traversal, as this would be complex to implement and can quickly leak a lot of memory when accumulating multiple partial queries.</p>
<p>All <code>ghc-debug-brick</code> heap traversals support this form of streaming/incremental results.
While <code>ghc-debug</code> can be slow because the heap traversals are slow, it now feels much more responsive and more rewarding to work with large heaps.</p>
<figure>
<img src="https://well-typed.com/blog/aux/images/ghc-debug-incremental/incremental_profile.gif" alt="Incremental results of a heap traversal" />
<figcaption aria-hidden="true"><a href="https://well-typed.com/blog/aux/images/ghc-debug-incremental/incremental_profile.gif">Incremental results of a heap traversal</a></figcaption>
</figure>
<h2 id="history-navigation">History navigation</h2>
<p>Heap traversals are expensive and take time, but <code>ghc-debug-brick</code> had only one view: the result of the last heap traversal.
This was a constant source of frustration when analysing huge heaps, making it tedious to make progress.
We improved this by adding a history for finished (and partial) heap traversals.
You can view the history by pressing <code>Ctrl+r</code>, or via the command picker, and see the results of previous queries.</p>
<p>The history can be navigated with <code>Alt+Left Arrow</code> to go back in history or <code>Alt+Right Arrow</code> to go forward again.</p>
<figure>
<img src="https://well-typed.com/blog/aux/images/ghc-debug-incremental/history_picker.png" alt="View the history of heap traversals" />
<figcaption aria-hidden="true"><a href="https://well-typed.com/blog/aux/images/ghc-debug-incremental/history_picker.png">View the history of heap traversals</a></figcaption>
</figure>
<h2 id="stack-decoding">Stack decoding</h2>
<p>We improved the stack frame decoding logic of <code>ghc-debug</code> considerably, adding support for underflow frames, fixing <code>InfoTable</code> decoding and tidying up the rendering.
Similar to other heap closures, stack frames have source locations (info table provenance entries) attached, which help with identifying where a particular stack frame or heap object was allocated.</p>
<p>Stack frames themselves can be retainers of heap objects, so improving <code>ghc-debug</code>’s support for stack frame decoding was much-needed.</p>
<figure>
<img src="https://well-typed.com/blog/aux/images/ghc-debug-incremental/better_stack_decoding.png" alt="Stack Frame decoding of Stack Trace annotated with source location labels" />
<figcaption aria-hidden="true"><a href="https://well-typed.com/blog/aux/images/ghc-debug-incremental/better_stack_decoding.png">Stack Frame decoding of Stack Trace annotated with source location labels</a><a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a></figcaption>
</figure>
<h3 id="stack-annotations">Stack annotations</h3>
<p>We added support for <a href="https://well-typed.com/blog/2025/09/better-haskell-stack-traces/">stack annotations</a> in supported GHC versions to <code>ghc-debug</code>.
These allow the user to annotate the native Haskell execution stack with additional metadata, such as
additional context about the current location in the source program.
Naturally, we taught <code>ghc-debug</code> how to decode stack annotation frames and display them as well.</p>
<p>As stack annotations are themselves merely heap objects, they are represented as <code>SomeStackAnnotation</code> closures that you can further drill into.
Unfortunately, the annotations themselves can’t be displayed simply via the usual <code>displayStackAnnotation</code> rendering function, as this might evaluate thunks and change the Haskell heap.</p>
<p>Stack annotations help to understand complicated computation flows by adding user-defined instrumentation to the RTS stack.
They were crucial in identifying multiple bugs in <code>ghc-debug</code>’s stack frame decoding logic.</p>
<figure>
<img src="https://well-typed.com/blog/aux/images/ghc-debug-incremental/stack_annotations.png" alt="Stack Frame decoding containing Stack Annotation Frames" />
<figcaption aria-hidden="true"><a href="https://well-typed.com/blog/aux/images/ghc-debug-incremental/stack_annotations.png">Stack Frame decoding containing Stack Annotation Frames</a></figcaption>
</figure>
<h2 id="conclusion">Conclusion</h2>
<p>We hope that the improvements to <code>ghc-debug</code> and <code>ghc-debug-brick</code> will aid the
workflows of anyone looking to perform detailed inspections of the heap of their
Haskell processes.
The improvements should especially benefit anyone who works with large heaps.
The new features have all been released to Hackage in <code>ghc-debug-brick-0.8</code>.</p>
<p>This work has been performed in collaboration with <a href="https://mercury.com/">Mercury</a>.
Mercury have a long-term commitment to the scalability and robustness of the Haskell
ecosystem and are supporting the development of memory profiling tools to
aid with these goals.</p>
<p>Well-Typed are always interested in projects and looking for funding to improve
GHC and other Haskell tools. Please contact <a href="mailto:info@well-typed.com">info@well-typed.com</a> if we
might be able to work with you!</p>
<section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes">
<hr />
<ol>
<li id="fn1"><p><code>Frame 0</code> is the top of the stack, while <code>Frame 23</code> is the bottom, indicated by the <code>STOP_FRAME</code> which is always the first stack frame on the GHC RTS stack.<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
</ol>
</section>
]]></content:encoded>
</item>
<item>
    <title>Real-Time Telemetry with Eventlog Live</title>
    <link>https://well-typed.com/blog/2026/09/real-time-telemetry-with-eventlog-live</link>
    <description><![CDATA[Well-Typed are happy to announce Eventlog Live, a program that streams real-time telemetry from any Haskell application to any observability platform that supports the OpenTelemetry protocol, such as Grafana Cloud, Prometheus, or local viewers such as otel-tui and otel-desktop-viewer.

In this blog post, [...]]]></description>
    <dc:creator>wen</dc:creator>
    <category>open-source</category>
    <category>telemetry</category>
    <category>performance</category>
    <category>profiling</category>
    <guid isPermaLink="false">http://www.well-typed.com/blog/2026/09/real-time-telemetry-with-eventlog-live</guid>
    <pubDate>Thu, 24 Sep 2026 00:00:00 GMT</pubDate>
    <content:encoded><![CDATA[<p>Well-Typed are happy to announce <a href="https://hackage.haskell.org/package/eventlog-live">Eventlog Live</a>, a program that streams real-time telemetry from any Haskell application to any observability platform that supports the OpenTelemetry protocol, such as <a href="https://grafana.com/">Grafana Cloud</a>, <a href="https://prometheus.io/docs/guides/opentelemetry/">Prometheus</a>, or local viewers such as <a href="https://github.com/ymtdzzz/otel-tui"><code>otel-tui</code></a> and <a href="https://github.com/CtrlSpice/otel-desktop-viewer"><code>otel-desktop-viewer</code></a>.</p>
<p>In this blog post, we’ll show a variety of ways to use Eventlog Live. For each showcase, we’ll link to the relevant instructions using Eventlog Live version <code>0.10.0.0</code> as well as the script that was used to generate the showcase. If you would like to use Eventlog Live, we recommend following the up-to-date instructions in <a href="https://hackage.haskell.org/package/eventlog-live#readme">the README of the latest release of Eventlog Live</a>.</p>
<!-- more -->
<div class="tile is-ancestor">
<div class="tile is-child box">
  <p class="is-sr-only" role="heading" aria-level="2">Heap Profiles by Closure Type</p>
  <img class="js-modal-trigger is-clickable has-ratio" width="1280" height="691" data-target="modal-oddball-with-pipe-2026-09-21" src="https://well-typed.com/blog/aux/images/real-time-telemetry-with-eventlog-live/oddball-with-pipe-2026-09-21.webp" alt="An animated GIF that shows a screen recording of a Grafana dashboard called Heap Profiles. It shows the heap profile of the example program called 'oddball', which has highly volatile memory usage. The dashboard has several configuration options, which let you select the service name and instance ID. For the example, the selected service name and instance ID are 'oddball' and a generated UUID, respectively. The dashboard is configured to show the last 5 minutes of data and refresh every 5 seconds. The dashboard is split into two columns. The left column contains a stacked area graph labelled 'Heap Profile' that shows a heap profile broken down by closure type. Below, there are three buttons labelled 'Start', 'Stop', and 'Census'. The right column contains six visualisations in a two-by-three grid. In order, these are labelled 'Heap Size', 'Memory Management', 'Heap Copied per GC by Generation', 'Heap Fragmentation by Generation', 'Heap Slop by Generation', and 'Heap Allocation by Capability'. The 'Heap Size' visualisation is a bar chart that shows three metrics. The 'Allocated from OS' metric shows the current size of the heap measured in megablocks, which is the unit in which the GHC RTS requests memory from the OS. The 'Allocated' metric shows the current size of the heap measured in blocks, which is the subunit that the GHC RTS uses to dole out memory to the application. The 'Live' metric shows the current size of the live heap, measured in live blocks. All three measurements are visualised in bytes. The 'Memory Management' visualisation shows three metrics that track the interaction with the OS: 'Current', 'Needed', and 'Returned'. The 'Current' metric shows the number of megablocks currently held by the RTS. The 'Needed' metric shows the number of megablocks that the RTS currently needs. If 'Needed' exceeds 'Current', the RTS is about to request more memory from the OS. If 'Needed' is less than 'Current', the RTS should return the excess megablocks to the OS. If this happens, the number of returned megablocks is shown in the 'Returned' metric. The 'Heap Copied per GC by Generation', 'Heap Fragmentation by Generation', and 'Heap Slop by Generation' are bar charts that show the number of bytes copied during GC, the amount of heap fragmentation found during GC, and the amount of slop bytes found during GC, respectively. These metrics are broken down by heap generations. In the example, these are Generation 0 and Generation 1. The 'Heap Allocation by Capability' visualisation is a bar chart that shows the number of bytes allocated by each capability. This metric is broken down by capability, but in the example there is only one Capability." />
  <p class="box">
    The Grafana Heap Profiles dashboard, which shows a heap profile broken down by closure type (<a href="https://ghc.gitlab.haskell.org/ghc/doc/users_guide/profiling.html#rts-options-heap-prof"><code>-hT</code></a>) as well as a variety of metrics for memory usage. For detailed instructions, see <a href="https://hackage.haskell.org/package/eventlog-live-0.10.0.0#eventlog-live--the-basic-version">Eventlog Live – The Basic Version</a>. This showcase uses the <a href="https://github.com/well-typed/eventlog-live/blob/eventlog-live-v0.10.0.0/examples/oddball/oddball-with-pipe.sh"><code>oddball-with-pipe</code></a> example with the Docker Compose setup in <a href="https://github.com/well-typed/eventlog-live/blob/eventlog-live-v0.10.0.0/demo/docker-compose.yml"><code>demo/docker-compose.yml</code></a>.
  </p>
</div>
</div>
<div class="modal" id="modal-oddball-with-pipe-2026-09-21" aria-hidden="true" aria-hidden="true">
  <div class="modal-background"></div>
  <div class="modal-content">
    <img loading="lazy" class="has-ratio" width="2560" height="1382" src="https://well-typed.com/blog/aux/images/real-time-telemetry-with-eventlog-live/oddball-with-pipe-2026-09-21-large.webp" alt="This is the same animated GIF as under Heap Profiles by Closure Type." />
    <p class="box notification">
      The Grafana Heap Profiles dashboard, which shows a heap profile broken down by closure type (<a href="https://ghc.gitlab.haskell.org/ghc/doc/users_guide/profiling.html#rts-options-heap-prof"><code>-hT</code></a>) as well as a variety of metrics for memory usage. For detailed instructions, see <a href="https://hackage.haskell.org/package/eventlog-live-0.10.0.0#eventlog-live--the-basic-version">Eventlog Live – The Basic Version</a>. This showcase uses the <a href="https://github.com/well-typed/eventlog-live/blob/eventlog-live-v0.10.0.0/examples/oddball/oddball-with-pipe.sh"><code>oddball-with-pipe</code></a> example with the Docker Compose setup in <a href="https://github.com/well-typed/eventlog-live/blob/eventlog-live-v0.10.0.0/demo/docker-compose.yml"><code>demo/docker-compose.yml</code></a>.
    </p>
  </div>
  <button class="modal-close is-large" aria-label="close"></button>
</div>
<h2 id="eventlog-live">Eventlog Live</h2>
<p>Eventlog Live analyses your application’s <a href="https://ghc.gitlab.haskell.org/ghc/doc/users_guide/runtime_control.html#rts-eventlog">eventlog</a> and exports it via the OpenTelemetry protocol.
Eventlog Live is <em>lightweight</em>, runs side-by-side with your application in only a few megabytes of memory, and requires <em>no instrumentation</em> other than the <a href="https://ghc.gitlab.haskell.org/ghc/doc/users_guide/phases.html#ghc-flag-threaded"><code>-threaded</code></a> and <a href="https://ghc.gitlab.haskell.org/ghc/doc/users_guide/phases.html#ghc-flag-rtsopts-none-some-all-ignore-ignoreAll"><code>-rtsopts</code></a> build options.<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a>
All you need is somewhere to send your data.
For instance, if you have a Grafana Cloud<a href="#fn2" class="footnote-ref" id="fnref2" role="doc-noteref"><sup>2</sup></a> account, adding telemetry to your application is as easy as:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode sh"><code class="sourceCode bash"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="co"># Create a pipe for the eventlog</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="va">EVENTLOG_PIPE</span><span class="op">=</span><span class="st">&quot;/tmp/eventlog.pipe&quot;</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="fu">mkfifo</span> <span class="st">&quot;</span><span class="va">${EVENTLOG_PIPE}</span><span class="st">&quot;</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a><span class="co"># Start your application</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a><span class="ex">your-application</span> +RTS <span class="at">-l</span> <span class="at">-ol</span><span class="st">&quot;</span><span class="va">${EVENTLOG_PIPE}</span><span class="st">&quot;</span> <span class="at">-hT</span> <span class="at">--eventlog-flush-interval</span><span class="op">=</span>1 <span class="at">-RTS</span> <span class="kw">&amp;</span></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a><span class="co"># Start eventlog-live-otlp</span></span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a><span class="va">OTEL_SERVICE_NAME</span><span class="op">=</span><span class="st">&quot;your-application&quot;</span>                                              <span class="dt">\</span></span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a><span class="va">OTEL_RESOURCE_ATTRIBUTES</span><span class="op">=</span><span class="st">&quot;service.instance.id=</span><span class="va">$(</span><span class="fu">uuidgen</span><span class="va">)</span><span class="st">&quot;</span>                         <span class="dt">\</span></span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a><span class="va">OTEL_EXPORTER_OTLP_ENDPOINT</span><span class="op">=</span><span class="st">&quot;https://otlp-gateway-&lt;YOUR_REGION&gt;.grafana.net/otlp&quot;</span> <span class="dt">\</span></span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a><span class="va">OTEL_EXPORTER_OTLP_HEADERS</span><span class="op">=</span><span class="st">&quot;Authorization=Basic%20&lt;YOUR_AUTHORIZATION_HEADER&gt;&quot;</span>    <span class="dt">\</span></span>
<span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a><span class="ex">eventlog-live-otlp</span> <span class="at">--eventlog-file</span><span class="op">=</span><span class="st">&quot;</span><span class="va">${EVENTLOG_PIPE}</span><span class="st">&quot;</span> <span class="at">-hT</span> <span class="at">--eventlog-flush-interval</span><span class="op">=</span>1</span></code></pre></div>
<p>Out of the box, Eventlog Live supports <a href="https://ghc.gitlab.haskell.org/ghc/doc/users_guide/profiling.html#rts-flag-hT"><code>-hT</code></a> and <a href="https://ghc.gitlab.haskell.org/ghc/doc/users_guide/profiling.html#rts-flag-hi"><code>-hi</code></a> heap profiles<a href="#fn3" class="footnote-ref" id="fnref3" role="doc-noteref"><sup>3</sup></a> as well as a variety of memory usage metrics, cost-centre stack profiles, log messages, productivity, detailed thread &amp; capability usage traces. Each of these features is showcased below using an example Grafana dashboard. These dashboards can be found in the repository under <a href="https://github.com/well-typed/eventlog-live/blob/eventlog-live-v0.10.0.0/demo-grafana-cloud/grafana-dashboards"><code>demo-grafana-cloud/grafana-dashboards</code></a> and <a href="https://github.com/well-typed/eventlog-live/blob/eventlog-live-v0.10.0.0/demo/config/grafana-dashboards"><code>demo/config/grafana-dashboards</code></a>. Click any showcase for more information.</p>
<div class="tile is-vertical is-ancestor">
  <div class="tile">
    <div class="tile is-child box" aria-hidden="true">
      <p class="title is-6" role="heading" aria-level="2">Heap Profiles by Closure Type</p>
      <img class="js-modal-trigger is-clickable has-ratio" width="1280" height="691" data-target="modal-oddball-with-pipe-2026-09-21" src="https://well-typed.com/blog/aux/images/real-time-telemetry-with-eventlog-live/oddball-with-pipe-2026-09-21.webp" alt="This is the same animated GIF as under Heap Profiles by Closure Type." />
    </div>
    <div class="tile is-child box">
      <p class="title is-6" role="heading" aria-level="2">Heap Profiles by Info Table</p>
      <img class="js-modal-trigger is-clickable has-ratio" width="1280" height="691" data-target="modal-oddball-with-hi-2026-09-21" src="https://well-typed.com/blog/aux/images/real-time-telemetry-with-eventlog-live/oddball-with-hi-2026-09-21.webp" alt="An animated GIF that shows a screen recording of the same Heap Profiles dashboard as under Heap Profiles by Closure Type, except that the categories for each heap profile sample show more detailed information based on the info tables." />
      <p class="is-sr-only">
        The Grafana Heap Profiles dashboard, which shows a variety of metrics for memory usage and a heap profile broken down by info table (<a href="https://ghc.gitlab.haskell.org/ghc/doc/users_guide/profiling.html#rts-options-heap-prof"><code>-hi</code></a>). For detailed instructions, see <a href="https://hackage.haskell.org/package/eventlog-live-0.10.0.0#eventlog-live-with-heap-profiling-by-info-table">Eventlog Live with Heap Profiling by Info Table</a>. This showcase uses the <a href="https://github.com/well-typed/eventlog-live/blob/eventlog-live-v0.10.0.0/examples/oddball/oddball-with-hi.sh"><code>oddball-with-hi</code></a> example with the Docker Compose setup in <a href="https://github.com/well-typed/eventlog-live/blob/eventlog-live-v0.10.0.0/demo/docker-compose.yml"><code>demo/docker-compose.yml</code></a>.
      </p>
    </div>
  </div>
  <div class="tile">
    <div class="tile is-child box">
      <p class="title is-6" role="heading" aria-level="2">Cost-Centre Stack Profiles</p>
      <img class="js-modal-trigger is-clickable has-ratio" width="1280" height="691" data-target="modal-jumpy-jump-with-cost-centre-profiler-2026-09-21" src="https://well-typed.com/blog/aux/images/real-time-telemetry-with-eventlog-live/jumpy-jump-with-cost-centre-profiler-2026-09-21.webp" alt="An animated GIF that shows a screen recording of a Grafana dashboard called Cost-Centre Stack Profiles. The dashboard has several configuration options, which let you select the service name and instance ID. For the example, the selected service name and instance ID are 'jumpy-jump' and a generated UUID, respectively. The dashboard is configured to show the last 6 hours of data and refresh every 5 seconds. The dashboard has one visualisation, which is split into two columns. The left column shows a table of symbols together with their sample count. The top two samples are IDLE and SYSTEM, followed by uniformIntegralM, an ever-changing list of ten functions called jumpyJump0 to jumpyJump9, and, occasionally, PROFILING:OVERHEAD_OF. The right column shows the stack profile as a flame graph. (Technically, an icicle chart, since the spikes go downward, but I don't think people use that word much anymore.) The flame graph is dominated by the IDLE category, which is sampled, roughly, two orders of magnitude more often than every other category." />
      <p class="is-sr-only">
        The Grafana Cost-Centre Stack Profiles dashboard, which shows the cost-centre stack samples collected by GHC's built-in profiler in a flame graph.
        For detailed instructions, see <a href="https://hackage.haskell.org/package/eventlog-live-0.10.0.0#eventlog-live-with-cost-centre-stack-profiling">Eventlog Live with Cost-Centre Stack Profiling</a>.
        This showcase uses the <a href="https://github.com/well-typed/eventlog-live/blob/eventlog-live-v0.10.0.0/examples/jumpy-jump/jumpy-jump-with-cost-centre-profiler.sh"><code>jumpy-jump-with-cost-centre-profiler</code></a> example with the Docker Compose setup in <a href="https://github.com/well-typed/eventlog-live/blob/eventlog-live-v0.10.0.0/demo/docker-compose.yml"><code>demo/docker-compose.yml</code></a>.
      </p>
    </div>
    <div class="tile is-child box">
      <p class="title is-6" role="heading" aria-level="2">Logs</p>
      <img class="js-modal-trigger is-clickable has-ratio" width="1280" height="691" data-target="modal-oddball-with-pipe-logs-2026-09-21" src="https://well-typed.com/blog/aux/images/real-time-telemetry-with-eventlog-live/oddball-with-pipe-logs-2026-09-21.webp" alt="An animated GIF that shows a screen recording of a Grafana dashboard called Logs. The dashboard has several configuration options, which let you select the service name and instance ID. For the example, the selected service name and instance ID are 'oddball' and a generated UUID, respectively. The dashboard is configured to show the last 6 hours of data and refresh every 5 seconds. The dashboard has one visualisation, which shows a list of log messages with their time and severity. Each log message has the TRACE severity and is some variant of 'Summing 313172 numbers', except the first two messages which are the thread labels for the timer and IOManager threads emitted by the RTS." />
      <p class="is-sr-only">
        The Grafana Logs dashboard, which shows log messages emitted using <a href="https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Debug-Trace.html#v:traceEventIO"><code>traceEventIO</code></a>, <a href="https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Debug-Trace.html#v:traceMarkerIO"><code>traceMarkerIO</code></a>, and <a href="https://hackage-content.haskell.org/package/base-4.22.0.0/docs/GHC-Conc-Sync.html#v:threadLabel"><code>threadLabel</code></a>.
        This showcase uses the <a href="https://github.com/well-typed/eventlog-live/blob/eventlog-live-v0.10.0.0/examples/oddball/oddball-with-pipe.sh"><code>oddball-with-pipe</code></a> example with the Docker Compose setup in <a href="https://github.com/well-typed/eventlog-live/blob/eventlog-live-v0.10.0.0/demo/docker-compose.yml"><code>demo/docker-compose.yml</code></a>.
      </p>
    </div>
    <div class="tile is-child box">
      <p class="title is-6" role="heading" aria-level="2">Logs as Markers</p>
      <img class="js-modal-trigger is-clickable has-ratio" width="1280" height="691" data-target="modal-oddball-with-hi-logs-as-markers-2026-09-21" src="https://well-typed.com/blog/aux/images/real-time-telemetry-with-eventlog-live/oddball-with-hi-logs-as-markers-2026-09-21.webp" alt="An animated GIF that shows a screen recording of the same Heap Profiles dashboard as under Heap Profiles by Info Table. However, in the configuration options a switch labelled UserMarker is enabled and the field labelled Filter UserMarker contains the regular expression 'Summing \d+ numbers', and each visualisation shows dotted red lines at the times that correspond to these markers." />
      <p class="is-sr-only">
        The Grafana Heap Profiles dashboard, which shows dotted red lines on each visualisation at the times that correspond to the markers, emitted using <a href="https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Debug-Trace.html#v:traceMarkerIO"><code>traceMarkerIO</code></a>, that match the regular expression <code>Summing \d+ numbers</code>.
        This showcase uses the <a href="https://github.com/well-typed/eventlog-live/blob/eventlog-live-v0.10.0.0/examples/oddball/oddball-with-hi.sh"><code>oddball-with-hi</code></a> example with the Docker Compose setup in <a href="https://github.com/well-typed/eventlog-live/blob/eventlog-live-v0.10.0.0/demo/docker-compose.yml"><code>demo/docker-compose.yml</code></a>.
      </p>
    </div>
  </div>
  <div class="tile">
    <div class="tile is-child box">
      <p class="title is-6" role="heading" aria-level="2">Productivity</p>
      <img class="js-modal-trigger is-clickable has-ratio" width="1280" height="691" data-target="modal-oddball-with-pipe-threads-2026-09-21" src="https://well-typed.com/blog/aux/images/real-time-telemetry-with-eventlog-live/oddball-with-pipe-threads-2026-09-21.webp" alt="An animated GIF that shows a screen recording of a Grafana dashboard called Threads. The dashboard has several configuration options, which let you select the service name and instance ID. For the example, the selected service name and instance ID are 'oddball' and a generated UUID, respectively. The dashboard is configured to show the last 5 minutes of data and refresh every 5 seconds. The dashboard is split into two columns. In the left column, there is a bar chart labelled  'Productivity by Capability', which shows the ratio between executing user code and garbage collection as a percentage, split out by capability and summed over the entire execution. Oddball uses only one capability, Capability 0. From the bar chart, we can see that this capability is about 80% productive during the first few seconds, then nosedives to about 65%, and stays there for the remainder of the animation. In the right column, there is a stacked area graph labelled 'Capability Usage', which shows the capability usage over a sliding window, split out into executing user code (labelled 'Mutator'), garbage collection (labelled 'GC'), and idle time (labelled 'Idle'). While this affirms the image shown by the previous visualisation, it reveals that oddball is idle the vast majority of the time." />
      <p class="is-sr-only">
        The Grafana Threads dashboard, which shows the cumulative productivity per capability, as well as the overall productivity over a sliding window.
        This showcase uses the <a href="https://github.com/well-typed/eventlog-live/blob/eventlog-live-v0.10.0.0/examples/oddball/oddball-with-pipe.sh"><code>oddball-with-pipe</code></a> example with the Docker Compose setup in <a href="https://github.com/well-typed/eventlog-live/blob/eventlog-live-v0.10.0.0/demo/docker-compose.yml"><code>demo/docker-compose.yml</code></a>.
      </p>
    </div>
    <div class="tile is-child box">
      <p class="title is-6" role="heading" aria-level="2">Thread & Capability Usage</p>
      <img class="js-modal-trigger is-clickable has-ratio" width="1280" height="691" data-target="modal-spectral-norm-thread-usage-2026-09-21" src="https://well-typed.com/blog/aux/images/real-time-telemetry-with-eventlog-live/spectral-norm-thread-usage-2026-09-21.png" alt="A screenshot that shows the Grafana Trace Explorer, which is currently exploring a thread usage trace for thread 3. Each span is listed with its type (either Running or Blocked) and its duration. One particular span is selected, which unfolds the entry and reveals more information. Notably, it reveals that thread 3 was blocked during this span due to a foreign function call." />
    </div>
  </div>
</div>
<div class="modal" id="modal-oddball-with-hi-2026-09-21" aria-hidden="true">
  <div class="modal-background"></div>
  <div class="modal-content">
    <img loading="lazy" class="has-ratio" width="2560" height="1382" src="https://well-typed.com/blog/aux/images/real-time-telemetry-with-eventlog-live/oddball-with-hi-2026-09-21-large.webp" alt="This is the same animated GIF as under Heap Profiles by Info Table." />
    <p class="box notification">
      The Grafana Heap Profiles dashboard, which shows a variety of metrics for memory usage and a heap profile broken down by info table (<a href="https://ghc.gitlab.haskell.org/ghc/doc/users_guide/profiling.html#rts-options-heap-prof"><code>-hi</code></a>). For detailed instructions, see <a href="https://hackage.haskell.org/package/eventlog-live-0.10.0.0#eventlog-live-with-heap-profiling-by-info-table">Eventlog Live with Heap Profiling by Info Table</a>. This showcase uses the <a href="https://github.com/well-typed/eventlog-live/blob/eventlog-live-v0.10.0.0/examples/oddball/oddball-with-hi.sh"><code>oddball-with-hi</code></a> example with the Docker Compose setup in <a href="https://github.com/well-typed/eventlog-live/blob/eventlog-live-v0.10.0.0/demo/docker-compose.yml"><code>demo/docker-compose.yml</code></a>.
    </p>
  </div>
  <button class="modal-close is-large" aria-label="close"></button>
</div>
<div class="modal" id="modal-jumpy-jump-with-cost-centre-profiler-2026-09-21" aria-hidden="true">
  <div class="modal-background"></div>
  <div class="modal-content">
    <img loading="lazy" class="has-ratio" width="2560" height="1382" src="https://well-typed.com/blog/aux/images/real-time-telemetry-with-eventlog-live/jumpy-jump-with-cost-centre-profiler-2026-09-21-large.webp" alt="This is the same animated GIF as under Cost-Centre Stack Profiler." />
    <p class="box notification">
      The Grafana Cost-Centre Stack Profiles dashboard, which shows the cost-centre stack samples collected by GHC's built-in profiler in a flame graph.
      For detailed instructions, see <a href="https://hackage.haskell.org/package/eventlog-live-0.10.0.0#eventlog-live-with-cost-centre-stack-profiling">Eventlog Live with Cost-Centre Stack Profiling</a>.
      This showcase uses the <a href="https://github.com/well-typed/eventlog-live/blob/eventlog-live-v0.10.0.0/examples/jumpy-jump/jumpy-jump-with-cost-centre-profiler.sh"><code>jumpy-jump-with-cost-centre-profiler</code></a> example with the Docker Compose setup in <a href="https://github.com/well-typed/eventlog-live/blob/eventlog-live-v0.10.0.0/demo/docker-compose.yml"><code>demo/docker-compose.yml</code></a>.
    </p>
  </div>
  <button class="modal-close is-large" aria-label="close"></button>
</div>
<div class="modal" id="modal-oddball-with-pipe-logs-2026-09-21" aria-hidden="true">
  <div class="modal-background"></div>
  <div class="modal-content">
    <img loading="lazy" class="has-ratio" width="2560" height="1382" src="https://well-typed.com/blog/aux/images/real-time-telemetry-with-eventlog-live/oddball-with-pipe-logs-2026-09-21-large.webp" alt="This is the same animated GIF as under Logs." />
    <p class="box notification">
      The Grafana Logs dashboard, which shows log messages emitted using <a href="https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Debug-Trace.html#v:traceEventIO"><code>traceEventIO</code></a>, <a href="https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Debug-Trace.html#v:traceMarkerIO"><code>traceMarkerIO</code></a>, and <a href="https://hackage-content.haskell.org/package/base-4.22.0.0/docs/GHC-Conc-Sync.html#v:threadLabel"><code>threadLabel</code></a>.
      This showcase uses the <a href="https://github.com/well-typed/eventlog-live/blob/eventlog-live-v0.10.0.0/examples/oddball/oddball-with-pipe.sh"><code>oddball-with-pipe</code></a> example with the Docker Compose setup in <a href="https://github.com/well-typed/eventlog-live/blob/eventlog-live-v0.10.0.0/demo/docker-compose.yml"><code>demo/docker-compose.yml</code></a>.
    </p>
  </div>
  <button class="modal-close is-large" aria-label="close"></button>
</div>
<div class="modal" id="modal-oddball-with-hi-logs-as-markers-2026-09-21" aria-hidden="true">
  <div class="modal-background"></div>
  <div class="modal-content">
    <img loading="lazy" class="has-ratio" width="2560" height="1382" src="https://well-typed.com/blog/aux/images/real-time-telemetry-with-eventlog-live/oddball-with-hi-logs-as-markers-2026-09-21-large.webp" alt="This is the same animated GIF as under Logs as Markers." />
    <p class="box notification">
      The Grafana Heap Profiles dashboard, which shows dotted red lines on each visualisation at the times that correspond to the markers, emitted using <a href="https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Debug-Trace.html#v:traceMarkerIO"><code>traceMarkerIO</code></a>, that match the regular expression <code>Summing \d+ numbers</code>.
      This showcase uses the <a href="https://github.com/well-typed/eventlog-live/blob/eventlog-live-v0.10.0.0/examples/oddball/oddball-with-hi.sh"><code>oddball-with-hi</code></a> example with the Docker Compose setup in <a href="https://github.com/well-typed/eventlog-live/blob/eventlog-live-v0.10.0.0/demo/docker-compose.yml"><code>demo/docker-compose.yml</code></a>.
    </p>
  </div>
  <button class="modal-close is-large" aria-label="close"></button>
</div>
<div class="modal" id="modal-oddball-with-pipe-threads-2026-09-21" aria-hidden="true">
  <div class="modal-background"></div>
  <div class="modal-content">
    <img loading="lazy" class="has-ratio" width="2560" height="1382" src="https://well-typed.com/blog/aux/images/real-time-telemetry-with-eventlog-live/oddball-with-pipe-threads-2026-09-21-large.webp" alt="This is the same animated GIF as under Productivity." />
    <p class="box notification">
      The Grafana Threads dashboard, which shows the cumulative productivity per capability, as well as the overall productivity over a sliding window.
      This showcase uses the <a href="https://github.com/well-typed/eventlog-live/blob/eventlog-live-v0.10.0.0/examples/oddball/oddball-with-pipe.sh"><code>oddball-with-pipe</code></a> example with the Docker Compose setup in <a href="https://github.com/well-typed/eventlog-live/blob/eventlog-live-v0.10.0.0/demo/docker-compose.yml"><code>demo/docker-compose.yml</code></a>.
    </p>
  </div>
  <button class="modal-close is-large" aria-label="close"></button>
</div>
<div class="modal" id="modal-spectral-norm-thread-usage-2026-09-21" aria-hidden="true">
  <div class="modal-background"></div>
  <div class="modal-content">
    <img loading="lazy" class="has-ratio" width="2560" height="1382" src="https://well-typed.com/blog/aux/images/real-time-telemetry-with-eventlog-live/spectral-norm-thread-usage-2026-09-21.png" alt="This is the same image as under Thread & Capability Usage." />
    <p class="box notification">
      The Tempo trace explorer, which shows a detailed trace of the scheduling of one thread, which includes the durations for each running/blocked segment as well as the reason for each time the thread becomes blocked.
      These traces are not enabled by default, as their volume easily overwhelms most endpoints, and must be enabled using a <a href="https://hackage.haskell.org/package/eventlog-live#configuration-files">configuration file</a>.
      This showcase uses the <a href="https://github.com/well-typed/eventlog-live/blob/eventlog-live-v0.10.0.0/examples/spectral-norm/spectral-norm.sh"><code>spectral-norm</code></a> example with the Docker Compose setup in <a href="https://github.com/well-typed/eventlog-live/blob/eventlog-live-v0.10.0.0/demo/docker-compose.yml"><code>demo/docker-compose.yml</code></a>.
    </p>
  </div>
  <button class="modal-close is-large" aria-label="close"></button>
</div>
<p>Eventlog Live’s OTLP exporter is configured using OpenTelemetry’s environment variables. The output of <code>eventlog-live-otlp --help</code> has <a href="https://github.com/well-typed/eventlog-live/blob/eventlog-live-v0.10.0.0/eventlog-live/data/help.footer.txt">comprehensive documentation on its support for these environment variables</a>.
By default, Eventlog Live aggregates telemetry data over 1 second intervals and exports every 30 seconds, but these intervals can be configured for each telemetry signal using <a href="https://hackage.haskell.org/package/eventlog-live#configuration-files">configuration files</a>.</p>
<h2 id="dynamic-control-with-eventlog-socket">Dynamic Control with Eventlog Socket</h2>
<p>If you instrument your application with <a href="https://hackage.haskell.org/package/eventlog-socket/#readme"><code>eventlog-socket</code></a>, you’ll be able to start/stop profiling while your application is running using a REST API, which you can connect, e.g., to buttons on your Grafana dashboard, as we’ve done on the example Heap Profiles dashboard.</p>
<div class="tile is-ancestor">
<div class="tile is-child box">
  <p class="is-sr-only" role="heading" aria-level="2">Dynamic Control with Eventlog Socket</p>
  <img class="js-modal-trigger is-clickable has-ratio" width="1280" height="691" data-target="modal-oddball-control-2026-09-21" src="https://well-typed.com/blog/aux/images/real-time-telemetry-with-eventlog-live/oddball-control-2026-09-21.webp" alt="An animated GIF that shows a screen recording of the same Grafana Heap Profiles dashboard that was described above.  It continues to show the heap profile of the 'oddball' example program.  This recording illustrates the usage of the 'Start' and 'Stop' buttons.   At the start of the recording, the 'Heap Profile' visualisation shows a history of volatile and spiky memory usage.  After a few refreshes, the 'Stop' button is pressed, the message 'Requested Heap Profiling Stop' appears in the top-right corner, and the 'Heap Profile' visualisation flatlines.   A few refreshes later, the 'Start' button is pressed, the message 'Requested Heap Profiling Start' appears in the top-right corner, and the 'Heap Profile' visualisation resumes.  The 'Census' button can be used to request a single heap census, though this isn't shown in the recording.  The visualisations in the right column are unaffected by the 'Start' and 'Stop' button, and continue to update during the entire recording." />
  <p class="box">
    Using the buttons on the Grafana Heap Profiles dashboard. When “Stop” is pressed, the heap profile flatlines. When “Start” is pressed, the heap profile resumes. The “Census” button can be used to request a single heap census. For detailed instructions, see <a href="https://hackage.haskell.org/package/eventlog-live-0.10.0.0#eventlog-live-with-eventlog-socket">Eventlog Live with Eventlog Socket</a>. This showcase uses the <a href="https://github.com/well-typed/eventlog-live/blob/eventlog-live-v0.10.0.0/examples/oddball/oddball-with-hT.sh"><code>oddball-with-hT</code></a> example with the Docker Compose setup in <a href="https://github.com/well-typed/eventlog-live/blob/eventlog-live-v0.10.0.0/demo/docker-compose.yml"><code>demo/docker-compose.yml</code></a>.
  </p>
</div>
</div>
<div class="modal" id="modal-oddball-control-2026-09-21" aria-hidden="true">
  <div class="modal-background"></div>
  <div class="modal-content">
    <img loading="lazy" class="has-ratio" width="2560" height="1382" src="https://well-typed.com/blog/aux/images/real-time-telemetry-with-eventlog-live/oddball-control-2026-09-21-large.webp" alt="This is the same animated GIF as under Dynamic Control with Eventlog Socket." />
    <p class="box">
      Using the buttons on the Grafana Heap Profiles dashboard. When “Stop” is pressed, the heap profile flatlines. When “Start” is pressed, the heap profile resumes. The “Census” button can be used to request a single heap census. For detailed instructions, see <a href="https://hackage.haskell.org/package/eventlog-live-0.10.0.0#eventlog-live-with-eventlog-socket">Eventlog Live with Eventlog Socket</a>. This showcase uses the  <a href="https://github.com/well-typed/eventlog-live/blob/eventlog-live-v0.10.0.0/examples/oddball/oddball-with-hT.sh"><code>oddball-with-hT</code></a> example with the Docker Compose setup in <a href="https://github.com/well-typed/eventlog-live/blob/eventlog-live-v0.10.0.0/demo/docker-compose.yml"><code>demo/docker-compose.yml</code></a>.
    </p>
  </div>
  <button class="modal-close is-large" aria-label="close"></button>
</div>
<p>Eventlog Socket lets your application write its eventlog over sockets and uses the other direction of communication for control messages. Eventlog Socket’s control protocol is generic. It isn’t bound to any specific application or library. Any library can register its own <a href="https://hackage-content.haskell.org/package/eventlog-socket-0.1.3.0/docs/GHC-Eventlog-Socket.html#g:control_commands">control commands</a> under its own namespace,<a href="#fn4" class="footnote-ref" id="fnref4" role="doc-noteref"><sup>4</sup></a> using Haskell IO actions as callbacks:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a>registerMyEventlogSocketSupport <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>  myPackageNamespace <span class="ot">&lt;-</span> registerNamespace <span class="st">&quot;my-package-name&quot;</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>  registerCommand myPackageNamespace (<span class="dt">CommandId</span> <span class="dv">1</span>) myCommandCallback1</span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a>  registerCommand myPackageNamespace (<span class="dt">CommandId</span> <span class="dv">2</span>) myCommandCallback2</span></code></pre></div>
<p>Any command that your application registers with Eventlog Socket can be invoked using Eventlog Live’s REST API.</p>
<p>For security reasons, Eventlog Socket’s control protocol and Eventlog Live’s REST API are hidden behind the <code>+control</code> feature flag, which is disabled by default. If you develop a library that (optionally) uses Eventlog Socket’s control protocol, we recommend using the same <code>+control</code> feature flag.</p>
<h2 id="call-stack-profiles-with-ghc-stack-profiler">Call-Stack Profiles with GHC Stack Profiler</h2>
<p>If you instrument your application with <a href="https://hackage.haskell.org/package/ghc-stack-profiler/#readme"><code>ghc-stack-profiler</code></a>, you’ll be able to use its lightweight call-stack profiler.</p>
<div class="tile is-ancestor">
<div class="tile is-child box">
  <p class="is-sr-only" role="heading" aria-level="2">Call-Stack Profiles with GHC Stack Profiler</p>
  <img class="js-modal-trigger is-clickable has-ratio" width="1280" height="691" data-target="modal-jumpy-jump-with-ghc-stack-profiler-2026-09-21" src="https://well-typed.com/blog/aux/images/real-time-telemetry-with-eventlog-live/jumpy-jump-with-ghc-stack-profiler-2026-09-21.webp" alt="An animated GIF that shows a screen recording of a Grafana dashboard called Call-Stack Profiles. This dashboard has the same layout as described under Cost-Centre Stack Profiler, but the profiles are slightly different. Notably, the IDLE, SYSTEM, and PROFILING:OVERHEAD_OF categories are not recorded by GHC Stack Profiler. The most prevalent category is 'other' followed by the same ever-changing list of ten functions called jumpyJump0 to jumpyJump9. The flame graph is significantly spikier. The IDLE category isn't recorded and does not dominate the flame graph, which leaves a lot more room for the deeply nested calls jumping between the various jumpyJump functions." />
  <p class="box">
    The Grafana Call-Stack Profiles dashboard, which shows the call-stack samples collected by <code>ghc-stack-profiler</code> in a flame graph. For detailed instructions, see <a href="https://hackage.haskell.org/package/eventlog-live-0.10.0.0#eventlog-live-with-ghc-stack-profiler">Eventlog Live with GHC Stack Profiler</a>. This showcase uses the <a href="https://github.com/well-typed/eventlog-live/blob/74a1c45451b1e3dac7a0c2ab826df22a7a7fe471/examples/jumpy-jump/jumpy-jump-with-ghc-stack-profiler.sh"><code>jumpy-jump-with-ghc-stack-profiler</code></a> example with the Docker Compose setup in <a href="https://github.com/well-typed/eventlog-live/blob/eventlog-live-v0.10.0.0/demo/docker-compose.yml"><code>demo/docker-compose.yml</code></a>.
  </p>
</div>
</div>
<div class="modal" id="modal-jumpy-jump-with-ghc-stack-profiler-2026-09-21" aria-hidden="true">
  <div class="modal-background"></div>
  <div class="modal-content">
  <figure>
    <img loading="lazy" class="has-ratio" width="2560" height="1382" src="https://well-typed.com/blog/aux/images/real-time-telemetry-with-eventlog-live/jumpy-jump-with-ghc-stack-profiler-2026-09-21-large.webp" alt="This is the same animated GIF as under Call-Stack Profiles with GHC Stack Profiler." />
    <figcaption class="box">
      The Grafana Call-Stack Profiles dashboard, which shows the call-stack samples collected by <code>ghc-stack-profiler</code> in a flame graph. For detailed instructions, see <a href="https://hackage.haskell.org/package/eventlog-live-0.10.0.0#eventlog-live-with-ghc-stack-profiler">Eventlog Live with GHC Stack Profiler</a>. This showcase uses the <a href="https://github.com/well-typed/eventlog-live/blob/74a1c45451b1e3dac7a0c2ab826df22a7a7fe471/examples/jumpy-jump/jumpy-jump-with-ghc-stack-profiler.sh"><code>jumpy-jump-with-ghc-stack-profiler</code></a> example with the Docker Compose setup in <a href="https://github.com/well-typed/eventlog-live/blob/eventlog-live-v0.10.0.0/demo/docker-compose.yml"><code>demo/docker-compose.yml</code></a>.
    </figcaption>
  </figure>
  </div>
  <button class="modal-close is-large" aria-label="close"></button>
</div>
<p>GHC Stack Profiler samples GHC’s runtime call-stack, rather than a virtual cost-centre stack, and writes these samples to the eventlog. Based on <a href="https://hackage.haskell.org/package/ghc-stack-profiler-0.5.0.0#benchmarks">our benchmarks</a>, GHC Stack Profiler has virtually no overhead when it’s not running and we observed 2-8% overhead while it was running, compared to GHC’s built-in cost-centre stack profiler, where we observed 54-128% overhead while it wasn’t running and 98-136% overhead while it was running.</p>
<p>If you’ve also instrumented your application with Eventlog Socket, you’ll be able to start/stop GHC Stack Profiler while your application is running using the same REST API, which you can connect, e.g., to buttons on your Grafana dashboard, as we’ve done on the example Call-Stack Profiles dashboard.</p>
<p>GHC Stack Profiler can also be used without Eventlog Live, e.g., by <a href="https://hackage.haskell.org/package/ghc-stack-profiler-0.5.0.0#ghc-stack-profiler-with-speedscope">exporting the profiles to speedscope</a>. We’ll discuss GHC Stack Profiler in detail in an upcoming blog post.</p>
<h2 id="local-viewers">Local Viewers</h2>
<p>Eventlog Live does not yet include a built-in telemetry viewer, as it was primarily developed to monitor long-running processes in production. We are currently developing a built-in telemetry viewer, but the prototype is not yet ready for publication. In the meantime, some developers may feel that using either Grafana Cloud or the Docker Compose setup in <a href="https://github.com/well-typed/eventlog-live/blob/eventlog-live-v0.10.0.0/demo/docker-compose.yml"><code>demo/docker-compose.yml</code></a> is too heavyweight for local development. Fortunately, the OpenTelemetry ecosystem has several lightweight telemetry viewers, such as <a href="https://github.com/ymtdzzz/otel-tui"><code>otel-tui</code></a> and <a href="https://github.com/CtrlSpice/otel-desktop-viewer"><code>otel-desktop-viewer</code></a>, which are general purpose telemetry viewers that use a text and browser-based UI, respectively.</p>
<div class="tile is-ancestor">
  <div class="tile is-child box">
    <p class="is-sr-only" role="heading" aria-level="2">Eventlog Live with otel-tui</p>
    <img class="js-modal-trigger is-clickable has-ratio" width="1280" height="691" data-target="modal-oddball-with-otel-tui-2026-09-22" src="https://well-typed.com/blog/aux/images/real-time-telemetry-with-eventlog-live/oddball-with-otel-tui-2026-09-22.webp" alt="An animated GIF that shows a screen recording of a terminal session. The terminal is split into three screens. On the left, taking up the majority of the terminal, is a screen labelled 'otel-tui'. On the right, split vertically, are two screens labelled 'oddball' and 'eventlog-live-otlp'. The 'otel-tui' screen is itself split into three subscreens. A narrow column on the left, labelled 'Metrics', lists all incoming metrics by name. These names correspond to the names described under Heap Profile by Closure Type, except formatted in camel-case and prefixed with 'ghc_eventlog_'. Currently, the metric named 'ghc_eventlog_HeapLive' is selected. The wide column on the right is split vertically. The top row, labelled 'Details', shows the exact structure of the selected OTLP message for the selected metric. The bottom row, labelled 'Chart', shows a text rendering of a line chart that shows the values of the 'ghc_eventlog_HeapLive' metric. This shows a spiky line, which jumps between 10-30MB, though the Y-axis is labelled in bytes rather than megabytes. The 'oddball' screen shows the output of the oddball program, which consists of many lines saying 'Generating X random numbers' and 'Sum: Y' for ever-changing values of X and Y. The 'eventlog-live-otlp' screen shows the debug output of Eventlog Live, which consists of many lines saying '[DEBUG] Received X events.', '[DEBUG] Exported Y metrics.', and '[DEBUG] Exported Z logs.', for ever-changing values of X, Y, and Z." />
    <p class="box">
      This showcase uses the <a href="https://github.com/well-typed/eventlog-live/blob/74a1c45451b1e3dac7a0c2ab826df22a7a7fe471/examples/oddball/oddball-with-otel-tui.sh"><code>oddball-with-otel-tui</code></a> example with <a href="https://github.com/ymtdzzz/otel-tui"><code>otel-tui</code></a> version 0.7.4.
    This example creates a screen session that combines <code>otel-tui</code> with <code>oddball</code> and <code>eventlog-live-otlp</code>.
    </p>
  </div>
  <div class="tile is-child box">
    <p class="is-sr-only" role="heading" aria-level="2">Eventlog Live with otel-desktop-viewer</p>
    <img class="js-modal-trigger is-clickable has-ratio" width="1280" height="691" data-target="modal-oddball-with-otel-desktop-viewer-2026-09-22" src="https://well-typed.com/blog/aux/images/real-time-telemetry-with-eventlog-live/oddball-with-otel-desktop-viewer-2026-09-22.webp" alt="An animated GIF that shows a screen recording of the browser-based otel-desktop-viewer application. The website is split into three columns. The left column has three tabs labelled 'Traces', 'Metrics', and 'Logs'. Currently, the 'Metrics' tab is selected, which lists all incoming metrics by name and application. These names correspond to the names described under Heap Profile by Closure Type, except formatted in camel-case and prefixed with 'ghc_eventlog_'. Currently, the metric named 'ghc_eventlog_HeapProfSample (oddball)' is selected. The right column lists all series in this metric. The 'ghc_eventlog_HeapProfSample' metric is the heap profile, broken down by closure type, so the various series correspond to the various closure types. Only the top few series are selected. Amongst these are built-in closure types such as FUN, FUN_2_0, STACK, and BLACKHOLE, as well as user-defined function types such as ghc-bignum:GHC.Num.Integer.IS. The series are constantly reordered, so they're a bit hard to read. The middle column, which takes up about half of the website, shows a chart with the selected series. While ten series are selected, only two lines are visible. STACK and ghc-bignum:GHC.Num.Integer.IS. This is because these samples represent the vast majority of the heap size used by the oddball program. The remaining lines, presumably, all hug the X-axis, though this isn't visually clear." />
    <p class="box">
      This showcase uses the <a href="https://github.com/well-typed/eventlog-live/blob/74a1c45451b1e3dac7a0c2ab826df22a7a7fe471/examples/oddball/oddball-with-otel-desktop-viewer.sh"><code>oddball-with-otel-desktop-viewer</code></a> example with <a href="https://github.com/CtrlSpice/otel-desktop-viewer"><code>otel-desktop-viewer</code></a> version 0.5.0.
    </p>
  </div>
</div>
<div class="modal" id="modal-oddball-with-otel-tui-2026-09-22" aria-hidden="true">
  <div class="modal-background"></div>
  <div class="modal-content">
    <img loading="lazy" class="has-ratio" width="2560" height="1382" src="https://well-typed.com/blog/aux/images/real-time-telemetry-with-eventlog-live/oddball-with-otel-tui-2026-09-22-large.webp" alt="This is the same animated GIF as under Eventlog Live with otel-tui." />
    <p class="box">
      This showcase uses the <a href="https://github.com/well-typed/eventlog-live/blob/74a1c45451b1e3dac7a0c2ab826df22a7a7fe471/examples/oddball/oddball-with-otel-tui.sh"><code>oddball-with-otel-tui</code></a> example with <a href="https://github.com/ymtdzzz/otel-tui"><code>otel-tui</code></a> version 0.7.4.
      This example creates a screen session that combines <code>otel-tui</code> with <code>oddball</code> and <code>eventlog-live-otlp</code>.
    </p>
  </div>
  <button class="modal-close is-large" aria-label="close"></button>
</div>
<div class="modal" id="modal-oddball-with-otel-desktop-viewer-2026-09-22" aria-hidden="true">
  <div class="modal-background"></div>
  <div class="modal-content">
    <img loading="lazy" class="has-ratio" width="2560" height="1382" src="https://well-typed.com/blog/aux/images/real-time-telemetry-with-eventlog-live/oddball-with-otel-desktop-viewer-2026-09-22-large.webp" alt="This is the same animated GIF as under Eventlog Live with otel-desktop-viewer." />
    <p class="box">
      This showcase uses the <a href="https://github.com/well-typed/eventlog-live/blob/74a1c45451b1e3dac7a0c2ab826df22a7a7fe471/examples/oddball/oddball-with-otel-desktop-viewer.sh"><code>oddball-with-otel-desktop-viewer</code></a> example with <a href="https://github.com/CtrlSpice/otel-desktop-viewer"><code>otel-desktop-viewer</code></a> version 0.5.0.
    </p>
  </div>
  <button class="modal-close is-large" aria-label="close"></button>
</div>
<h2 id="future-work">Future Work</h2>
<p>We are currently developing a built-in telemetry viewer that is purpose-built for GHC’s telemetry, with the intention of making Eventlog Live more useful for short development sessions. We’re also planning to create a library for writing structured log messages to the eventlog, expand the analyses supported by Eventlog Live, and expand the Eventlog Socket protocol to permit command arguments. Moreover, we’re always open to suggestions and feature requests from the community.</p>
<h2 id="acknowledgements">Acknowledgements</h2>
<p>This work has been performed in collaboration with Mercury, who have a long-term commitment to the scalability and robustness of the Haskell ecosystem. Well-Typed are always interested in projects and looking for funding to improve Haskell tools. Please contact <a href="mailto:info@well-typed.com">info@well-typed.com</a> if we might be able to work with you!</p>
<section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes">
<hr />
<ol>
<li id="fn1"><p>If your application is built with GHC 9.2 or older, it must also be built with <a href="https://ghc.gitlab.haskell.org/ghc/doc/users_guide/phases.html#ghc-flag-eventlog"><code>-eventlog</code></a>. Since GHC 9.4, the eventlog is enabled by default.<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn2"><p>The repository contains <a href="https://github.com/well-typed/eventlog-live/blob/eventlog-live-v0.10.0.0/demo-grafana-cloud#readme">a self-contained demo for using Eventlog Live with Grafana Cloud</a>. Unfortunately, as Grafana Cloud changes its user interface frequently, that demo may not be fully up-to-date.<a href="#fnref2" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn3"><p>Eventlog Live is developed and tested with the <a href="https://ghc.gitlab.haskell.org/ghc/doc/users_guide/profiling.html#rts-flag-hT"><code>-hT</code></a> and <a href="https://ghc.gitlab.haskell.org/ghc/doc/users_guide/profiling.html#rts-flag-hi"><code>-hi</code></a> heap profile breakdowns, but the <a href="https://ghc.gitlab.haskell.org/ghc/doc/users_guide/profiling.html#rts-flag-hm"><code>-hm</code></a>/<a href="https://ghc.gitlab.haskell.org/ghc/doc/users_guide/profiling.html#rts-flag-hd"><code>-hd</code></a>/<a href="https://ghc.gitlab.haskell.org/ghc/doc/users_guide/profiling.html#rts-flag-hy"><code>-hy</code></a>/<a href="https://ghc.gitlab.haskell.org/ghc/doc/users_guide/profiling.html#rts-flag-he"><code>-he</code></a> heap profile breakdowns should work. The <a href="https://ghc.gitlab.haskell.org/ghc/doc/users_guide/profiling.html#rts-flag-hc"><code>-hc</code></a>/<a href="https://ghc.gitlab.haskell.org/ghc/doc/users_guide/profiling.html#rts-flag-hb"><code>-hb</code></a>/<a href="https://ghc.gitlab.haskell.org/ghc/doc/users_guide/profiling.html#rts-flag-hr"><code>-hr</code></a> heap profile breakdowns require additional support, which is planned but not yet implemented.<a href="#fnref3" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn4"><p>We strongly recommend using your package name.<a href="#fnref4" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
</ol>
</section>
]]></content:encoded>
</item>
<item>
    <title>Haskell ecosystem activities report: June–August 2026</title>
    <link>https://well-typed.com/blog/2026/09/haskell-ecosystem-report-june-august-2026</link>
    <description><![CDATA[This is the thirty-second edition of our Haskell ecosystem activities report,
which describes the work Well-Typed are doing on GHC, Cabal, HLS and other parts
of the core Haskell toolchain. The current edition covers roughly the months of
June, July and August 2026.

You can find the previous editions [...]]]></description>
    <dc:creator>adam, alex, andrea, andreask, ben, davide, duncan, hannes, magnus, mikolaj, rodrigo, sam, tobias, wen, wolfgang, zubin</dc:creator>
    <category>well-typed</category>
    <category>ghc</category>
    <category>community</category>
    <category>haskell-ecosystem-report</category>
    <guid isPermaLink="false">http://www.well-typed.com/blog/2026/09/haskell-ecosystem-report-june-august-2026</guid>
    <pubDate>Fri, 18 Sep 2026 00:00:00 GMT</pubDate>
    <content:encoded><![CDATA[<p>This is the thirty-second edition of our Haskell ecosystem activities report,
which describes the work Well-Typed are doing on GHC, Cabal, HLS and other parts
of the core Haskell toolchain. The current edition covers roughly the months of
June, July and August 2026.</p>
<p>You can find the previous editions collected under the
<a href="https://well-typed.com/blog/tags/haskell-ecosystem-report">haskell-ecosystem-report tag</a>.</p>
<h2 id="sponsorship">Sponsorship</h2>
<p>We offer <a href="https://well-typed.com/ecosystem/">Haskell Ecosystem Support Packages</a> to provide commercial
users with support from Well-Typed’s experts while investing in the Haskell
community and its technical ecosystem including through the work described in
this report. To find out more, read our <a href="https://well-typed.com/blog/2025/06/haskell-ecosystem-support-packages">announcement of these
packages</a> in partnership with
the Haskell Foundation. We need funding to continue this essential maintenance work!</p>
<p>Many thanks to our Haskell Ecosystem Supporters: <a href="https://www.sc.com/">Standard Chartered</a>,
<a href="https://www.channable.com/">Channable</a> and <a href="https://qbaylogic.com/">QBayLogic</a>,
as well as to our other clients who also contribute to making this work possible:
<a href="https://www.anduril.com/">Anduril</a>, <a href="https://juspay.in/">Juspay</a> and <a href="https://mercury.com/">Mercury</a>;
and to the <a href="https://opencollective.com/haskell-language-server">HLS Open Collective</a> for
supporting HLS release management.</p>
<h2 id="highlights">Highlights</h2>
<!-- more -->
<h3 id="known-key-names-and-reinstallable-base">Known-key names and reinstallable <code>base</code></h3>
<p>We have long been working to decouple GHC from <code>base</code>, to make it easier to
upgrade the compiler independently from the (standard) libraries.
A key restriction that prevented disentangling <code>base</code> from GHC was GHC’s
treatment of <em>known entities</em>. These are things in the standard libraries
that GHC needs to know something about (e.g. <code>Show</code> for <code>deriving</code>, <code>Monad</code> for desugaring <code>do</code>
notation, etc). This is detailed in <a href="https://blog.haskell.org/making-ghc-upgrades-easy/#3-3-known-entities">section 3.3</a>
of <a href="https://blog.haskell.org/making-ghc-upgrades-easy/">a recent blog post by Simon Peyton Jones</a>.</p>
<p>Rodrigo and Sam recently overhauled GHC’s treatment of known entities, in
collaboration with Simon (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15899">!15899</a>), finally landing the patch in August. This
allowed known entities to be defined completely outside of the compiler for the
first time, without hard-coding the precise module that defines them.</p>
<p>As part of this change, it was also necessary to rework some implementation
details of <code>Typeable</code>, namely the <code>KindRep</code> datatype. Sam opened
<a href="https://github.com/haskell/core-libraries-committee/issues/433">CLC proposal <span>#433</span></a>
to get approval on this change, which required an accompanying change to the <code>binary</code>
package, which Rodrigo implemented in <a href="https://github.com/haskell/binary/pull/244"><span>#244</span></a>.</p>
<h3 id="aarch64-code-generation-and-sub-word-handling">AArch64 code generation and sub-word handling</h3>
<p>Andreas has been hunting down a slew of correctness bugs in the AArch64 native
code generator, mostly related to the handling of sub-word values. Issues
include sub-word handling across C calls (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/27430">#27430</a>), in atomic loads/stores
(<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/27533">#27533</a>), in conversions (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/27537">#27537</a>), in <code>if</code> conditionals (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/27538">#27538</a>), in <code>%i2f64</code>
(<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/27539">#27539</a>) and the use of invalid immediates (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/27550">#27550</a>). These were all fixed in
<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16255">!16255</a>, with a lot of the testing infrastructure being provided by Sam’s
overhaul of <code>test-primops</code>
(<a href="https://gitlab.haskell.org/ghc/test-primops/-/merge_requests/43"><span>!43</span></a>).</p>
<h3 id="coercion-zapping">Coercion zapping</h3>
<p>Extremely poor compiler performance for programs involving significant
type-level computation has long plagued the community (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/8095">#8095</a>, recently
re-reported as <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/27336">#27336</a>). One major cause for this is blow-ups in coercion sizes
due to the representation of accumulated type family reduction steps in Core,
GHC’s typed intermediate language.</p>
<p>This problem resisted many attempts over the years, with promising approaches
being brought down by performance regressions on certain types of programs (e.g.
singleton-heavy programs).
However, the future is bright for type-level wizards: MR <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16378">!16378</a> implementing
coercion zapping promises to bring the story to a satisfying conclusion.</p>
<p>This MR has an unusual history. In July, Sylvain Henry put up a <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/8095#note_685609">proof-of-concept patch</a>
that was generated using Claude Fable. This patch was a carefully balanced
“best of” previous approaches over the years, combining elements from MRs by
Ben (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/611">!611</a>), Adam (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/6476">!6476</a>), Sam (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/7787">!7787</a>) and Richard Eisenberg (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/7909">!7909</a>), each of
which made significant progress on some programs but struggled to overcome
significant compiler performance regressions on other programs.</p>
<p>Sam took over this patch and rewrote it from the ground up, doing a deep
dive into GHC’s type family rewriter, and finding
several related optimisation opportunities (e.g. <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16479">!16479</a>).</p>
<p>The net result is a patch that significantly improves compilation speed of
type-family-heavy programs without regressing elsewhere. This patch is under
final review and we hope to land it soon. Performance results
from GHC’s testsuite:</p>
<table>
<colgroup>
<col style="width: 23%" />
<col style="width: 40%" />
<col style="width: 36%" />
</colgroup>
<thead>
<tr class="header">
<th>Test case</th>
<th>Compiler allocations</th>
<th>Compilation time</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>T8095</td>
<td>  768 MB ⇝   128 MB (−83%)</td>
<td>2.432s ⇝ 0.160s (−93%)</td>
</tr>
<tr class="even">
<td>T9872a</td>
<td>1 656 MB ⇝ 1 406 MB (−15%)</td>
<td>0.971s ⇝ 0.494s (−49%)</td>
</tr>
<tr class="odd">
<td>T12545</td>
<td>  698 MB ⇝   425 MB (−39%)</td>
<td>0.648s ⇝ 0.387s (−40%)</td>
</tr>
<tr class="even">
<td>T13386</td>
<td>  428 MB ⇝    35 MB (−92%)</td>
<td>0.259s ⇝ 0.104s (−60%)</td>
</tr>
<tr class="odd">
<td>T27336</td>
<td>  499 MB ⇝    36 MB (−93%)</td>
<td>1.188s ⇝ 0.110s (−91%)</td>
</tr>
<tr class="even">
<td>CoOpt_Singletons</td>
<td>  634 MB ⇝   596 MB ( −6%)</td>
<td>0.484s ⇝ 0.360s (−26%)</td>
</tr>
<tr class="odd">
<td>LargeRecord</td>
<td>1 227 MB ⇝   967 MB (−21%)</td>
<td>0.872s ⇝ 0.617s (−29%)</td>
</tr>
<tr class="even">
<td>FamAppCachePerf</td>
<td>  496 MB ⇝   192 MB (−61%)</td>
<td>0.675s ⇝ 0.191s (−72%)</td>
</tr>
</tbody>
</table>
<h3 id="exception-annotations">Exception annotations</h3>
<p>Ben’s <a href="https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/0330-exception-backtraces.rst">GHC proposal <span>#330</span></a> and accompanying <a href="https://github.com/haskell/core-libraries-committee/issues/164">CLC proposal <span>#164</span></a>
proposed a deep rework of exception backtraces, providing several different
backtrace mechanisms (e.g. <code>HasCallStack</code>, profiling cost centres, IPE).</p>
<p>However, the implementation of these proposals remained patchy, as detailed in
<a href="https://well-typed.com/blog/2026/05/lay-annotation-land/">Edsko’s recent blog post</a>.</p>
<p>The team made several follow-up improvements to mechanisms for
attaching information to exceptions:</p>
<ul>
<li>Ben fixed <code>WhileHandling</code> dropping the <code>ExceptionContext</code> of the exception
it is handling (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16275">!16275</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/27456">#27456</a>).</li>
<li>Sam made sure <code>SomeException</code> does not drop the <code>ExceptionContext</code> of the
exception it is storing (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16274">!16274</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/27455">#27455</a>), with accompanying <a href="https://github.com/haskell/core-libraries-committee/issues/432">CLC proposal <span>#432</span></a>.</li>
<li>Wolfgang added <code>rethrowSTM</code> (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16501">!16501</a>), incorporated the re-export into the
<code>stm</code> submodule (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16616">!16616</a>), and upstreamed it to <code>stm</code> (<a href="https://github.com/haskell/stm/pull/100"><span>#100</span></a>),
with accompanying <a href="https://github.com/haskell/core-libraries-committee/issues/436">CLC proposal <span>#436</span></a>.</li>
<li>Zubin made <code>annotateSTM</code> use <code>catchSTM#</code> rather than <code>catch#</code> (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16508">!16508</a>),
with accompanying <a href="https://github.com/haskell/core-libraries-committee/issues/437">CLC proposal <span>#437</span></a>.</li>
</ul>
<h3 id="trees-that-grow">Trees That Grow</h3>
<p>Over a long period, GHC has been gradually migrating to the Trees That Grow
representation for its internal AST datatypes, and improving the modularity of
the AST within the GHC codebase (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/19218">#19218</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/21592">#21592</a>). Once complete, this will make
it possible to expose the AST in a package that does not depend on the rest of
the <code>ghc</code> library, and unblocks doing the same for the parser. As well as
improving modularity, this will make it easier to use in tools that parse
Haskell syntax, and may unlock future simplifications to <code>template-haskell</code>.</p>
<p>Alex and Rodrigo have been working on the next steps in that process,
reorganising the codebase so that there will be no modules in the
<code>Language.Haskell.*</code> namespace depending on modules in the <code>GHC.*</code> namespace.
This included decoupling <code>Language.Haskell.Syntax</code> from <code>GHC.Hs.Doc</code> (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15601">!15601</a>)
and from <code>GHC.Types.SourceText</code> (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15573">!15573</a>), representing string literals in
the AST with <code>Text</code> rather than <code>FastString</code> (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/21628">#21628</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/12846">!12846</a>), and decoupling
<code>ModuleName</code>s used in the AST from those in GHC (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16546">!16546</a>). As part of the
original migration strategy, various orphan instances were introduced, which Alex has
been cleaning up (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/27755">#27755</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16299">!16299</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16610">!16610</a>).</p>
<h3 id="gitlab-infrastructure-work">GitLab infrastructure work</h3>
<p>Keeping Haskell’s infrastructure available has been increasingly challenging over the
past couple of months due to the continued rise in LLM-driven crawling. See
for comparison the <a href="https://people.kernel.org/monsieuricon/creepy-crawlies"><em>Creepy crawlies</em> blog post by Konstantin Ryabitsev</a>,
detailing the challenges faced by the git hosting infrastructure for the Linux
kernel.</p>
<p>The GHC GitLab instance relies on <a href="https://github.com/TecharoHQ/anubis">Anubis</a>
to reduce crawler traffic. In early August, Magnus traced a fresh surge in load
to a crawler that was bypassing Anubis entirely. Blocking it cut the load on the
GitLab instance dramatically.</p>
<p>Other important infrastructure items:</p>
<ul>
<li><p>Magnus and Ben upgraded the instance’s PostgreSQL database, after which Ben
carried the GitLab upgrade itself through to the latest release in a single
fourteen-hour session. Besides clearing a long-standing backlog item, this
made it possible to prioritise traffic from logged-in users.</p></li>
<li><p>Magnus upgraded the host running the GitLab container, and applied patches
to the GitLab instance (e.g. to close certain security vulnerabilities).</p></li>
</ul>
<h2 id="ghc">GHC</h2>
<h3 id="releases">Releases</h3>
<ul>
<li>Andreas posted an <a href="https://discourse.haskell.org/t/2026-06-09-informal-ghc-release-status-update-of-a-sort/14250">informal GHC release status update</a> on Discourse in June.
GHC release progress is officially tracked on the <a href="https://gitlab.haskell.org/ghc/ghc/-/wikis/GHC-Status">GHC release status page</a>.</li>
<li>Zubin worked on 9.14.2, cutting a <a href="https://www.haskell.org/ghc/blog/20260730-ghc-9.14.2-rc1-released.html">first release candidate</a> at the end of July, and a <a href="https://www.haskell.org/ghc/blog/20260917-ghc-9.14.2-rc2-released.html">second release candidate</a> in September. This work fixed a significant number of bugs, many of them blockers for the next major release, 10.0, which Zubin will prepare in due course.</li>
<li>Magnus prepared <a href="https://www.haskell.org/ghc/blog/20260731-ghc-9.12.5-rc3-released.html">release candidates for 9.12.5</a>.</li>
<li>Andreas continued work backporting patches to the 9.10.4 branch.</li>
</ul>
<h3 id="frontend">Frontend</h3>
<ul>
<li><p>Magnus renamed <code>ZonkAny</code> to <code>UnusedType</code> and added pretty-printing
logic for it (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16212">!16212</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/27390">#27390</a>), so that it no longer leaks confusingly
in HLS output.</p></li>
<li><p>Magnus refactored the error reporting code for
<code>ExplicitLevelImports</code> (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16195">!16195</a>).</p></li>
<li><p>Adam marked various language extension flags as deprecated (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16145">!16145</a>),
implementing part of the categorisation of language extensions (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/27329">#27329</a>).</p></li>
<li><p>Sam relaxed representation-polymorphism restrictions on newtype
constructor patterns (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16342">!16342</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/20363">#20363</a>).</p></li>
<li><p>Sam fixed a couple of issues with mis-oriented coercions produced by GHC
(<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16474">!16474</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/27639">#27639</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16207">!16207</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/27374">#27374</a>).</p></li>
<li><p>Sam fixed a crash in the <code>Data.Data</code> instance for <code>HsCtxt</code> (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16142">!16142</a>,
<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/27359">#27359</a>).</p></li>
<li><p>Sam reviewed a patch adding explanations for unsolved <code>Typeable</code>
constraints (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16351">!16351</a>).</p></li>
</ul>
<h3 id="core-simplifier">Core &amp; simplifier</h3>
<ul>
<li><p>Andreas stopped the specialiser looping on recursive dictionaries
in <code>interestingDict</code> (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16559">!16559</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/27705">#27705</a>).</p></li>
<li><p>Zubin landed various fixes for “Entered absent arg” bugs
(<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16519">!16519</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/27627">#27627</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16503">!16503</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/27626">#27626</a>), along with fixes for the interaction of
absent fillers with speculative evaluation of dictionaries (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16219">!16219</a>),
following an extensive investigation of the crash reported in <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/25924">#25924</a>.</p></li>
<li><p>Following on from Simon Peyton Jones’ earlier refactor of <code>GHC.Core.Utils.mkTick</code>
(<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15494">!15494</a>), which Sam helped with, Sam and Magnus tied up several loose ends:</p>
<ul>
<li><p>preserving tick ordering in <code>tickTickedExpr</code> (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16601">!16601</a>), fixing a
regression in which source notes were moved past each other;</p></li>
<li><p>allowing more ticks in argument position in <code>rebuild_app</code>
(<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16558">!16558</a>);</p></li>
<li><p>stopping ticks being dropped around variables of type <code>IO ()</code>
(<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16123">!16123</a>), fixing <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/27182">#27182</a> and <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/27225">#27225</a>;</p></li>
<li><p>stopping <code>mkTick</code> in Core Prep from
breaking ANF (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16198">!16198</a>).</p></li>
</ul></li>
</ul>
<h3 id="driver">Driver</h3>
<ul>
<li><p>Rodrigo made some improvements to the structure and caching of downsweep
(<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16330">!16330</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/27053">#27053</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/27461">#27461</a>).</p>
<p>During this work, Rodrigo and Sam found an opportunity to clean up some
legacy Backpack logic, establishing that <code>implicitRequirementsShallow</code> can
never reach a <code>HoleUnit</code> (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16346">!16346</a>).</p></li>
<li><p>Rodrigo deduplicated and shared <code>Module</code>s in interface files (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16216">!16216</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16213">!16213</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/27401">#27401</a>).</p></li>
<li><p>Hannes shared <code>UnitInfo</code>s across the <code>UnitEnv</code> using a <code>UnitIndex</code>
and an external unit database cache (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16115">!16115</a>), and arranged for the
<code>UnitIndexCache</code> to be reused after initialising multiple home units (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16598">!16598</a>,
<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/27748">#27748</a>).</p></li>
<li><p>Hannes added the <code>backendInfoTableMapValidity</code> backend predicate
(<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16234">!16234</a>).</p></li>
<li><p>Hannes fixed the order of <code>PackageDBFlag</code>s for the interactive home unit
(<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16591">!16591</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/27640">#27640</a>).</p></li>
<li><p>Hannes diagnosed the quadratic behaviour of <code>ghc-pkg list</code> (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/27275">#27275</a>).</p></li>
<li><p>Zubin carried out a substantial overhaul of the loader (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16493">!16493</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16582">!16582</a>,
<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16583">!16583</a>) to allow more control over unloading stale linkables, which is
useful for HLS to efficiently evaluate expressions as files change in the
IDE (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/27606">#27606</a>).</p></li>
<li><p>Zubin made <code>UniqDFM.alter</code> preserve insertion order (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16385">!16385</a>).</p></li>
<li><p>Wolfgang finished up a patch sorting <code>Usage</code>s by fingerprint, to ensure
determinism (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15484">!15484</a>).</p></li>
</ul>
<h3 id="code-generation">Code generation</h3>
<ul>
<li>Andreas fixed common block elimination silently dropping a procedure’s info
table (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16592">!16592</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/27722">#27722</a>).</li>
</ul>
<h3 id="ghci-and-bytecode">GHCi and bytecode</h3>
<ul>
<li>Wolfgang added <code>--show-byte-code</code>, the counterpart of <code>--show-iface</code> for
bytecode files (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16386">!16386</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26909">#26909</a>).</li>
</ul>
<h3 id="rts">RTS</h3>
<ul>
<li><p>Duncan continued his sustained work to overhaul RTS I/O managers and the
scheduler. One of the main goals is to bring the non-threaded RTS in line
with the threaded one (e.g. <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/27335">#27335</a>), and to improve maintainability across
I/O manager implementations.
As part of this effort, Duncan landed a collection of I/O manager refactors
(<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16217">!16217</a>), preparing the ground for fixing idle GC and deadlock detection on the
non-threaded RTS. Duncan also brought the POSIX and Win32 ticker implementations
more in line (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16023">!16023</a>).</p></li>
<li><p>Andreas fixed pointer arithmetic in the RTS linker’s
<code>flushInstructionCacheRISCV64</code> (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16419">!16419</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/27569">#27569</a>).</p></li>
</ul>
<h3 id="build-system">Build system</h3>
<ul>
<li><p>Sam made Hadrian check for <code>cabal-install &gt;= 3.12</code> upfront (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16132">!16132</a>,
<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/27317">#27317</a>), rather than failing obscurely later on.</p></li>
<li><p>David fixed the <code>ghc-internal</code> <code>.def</code> file name (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16190">!16190</a>), removed
unused wrapper scripts from the Windows bindist (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16138">!16138</a>), and made Hadrian
avoid response files when the command line is short enough (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16134">!16134</a>),
as response files were impeding developer workflows (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/27230">#27230</a>).</p></li>
<li><p>David stopped <code>ghc-toolchain</code> throwing an exception when candidate executables are
not found (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16173">!16173</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/27369">#27369</a>).</p></li>
</ul>
<h3 id="testsuite">Testsuite</h3>
<ul>
<li><p>Sam made the test driver normalise call-stack line numbers when referring to
locations in libraries (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16247">!16247</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/27387">#27387</a>), so that tests are no longer
sensitive to irrelevant internal changes.</p></li>
<li><p>Andreas made the <code>process009</code> test use <code>SIGKILL</code> (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16429">!16429</a>).</p></li>
<li><p>Zubin made various improvements to the JUnit output, most importantly
showing diffs in the GitLab summary (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16309">!16309</a>) and reporting fragile failures as
skipped (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16250">!16250</a>). He also landed assorted testsuite fixes (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16413">!16413</a>).</p></li>
</ul>
<h3 id="ci-and-infrastructure">CI and infrastructure</h3>
<ul>
<li><p>Magnus worked on keeping nightly CI passing (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16296">!16296</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/27127">#27127</a>),
adding a retry when fetching test metrics (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16281">!16281</a>).</p></li>
<li><p>David made the <code>lint-changelog</code> job check for the <code>no-changelog</code>
label in the script rather than in <code>rules</code> (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16177">!16177</a>), which avoids having to
re-run the entire pipeline when applying the <code>no-changelog</code> label.</p></li>
<li><p>Zubin arranged for tool messages to be reported as errors or warnings depending
on the tool’s exit code, rather than always as errors (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16170">!16170</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/27370">#27370</a>).</p></li>
</ul>
<h3 id="process-library"><code>process</code> library</h3>
<ul>
<li>Zubin fixed link errors on certain macOS configurations by allowing
<code>posix_spawn_file_actions_addchdir</code> to be missing when linking against
<code>process</code> on Darwin (<a href="https://github.com/haskell/process/pull/377"><span>#377</span></a>,
<a href="https://github.com/haskell/process/issues/376"><span>#376</span></a>).</li>
</ul>
<h2 id="cabal">Cabal</h2>
<h3 id="releases-1">Releases</h3>
<ul>
<li>The 3.18.1.0 release was managed by Artem Pelenitsyn, with release engineering
help from Mikolaj (<a href="https://github.com/haskell/cabal/pull/12104"><span>#12104</span></a>,
<a href="https://github.com/haskell/cabal/pull/12128"><span>#12128</span></a>,
<a href="https://github.com/haskell/cabal/pull/12016"><span>#12016</span></a>,
<a href="https://github.com/haskell/cabal/pull/12010"><span>#12010</span></a>,
<a href="https://github.com/haskell/cabal/pull/12017"><span>#12017</span></a>,
<a href="https://github.com/haskell/cabal/pull/12108"><span>#12108</span></a>,
<a href="https://github.com/haskell/cabal/pull/12113"><span>#12113</span></a>,
<a href="https://github.com/haskell/cabal/pull/12182"><span>#12182</span></a>).</li>
</ul>
<h3 id="recompilation-checking">Recompilation checking</h3>
<ul>
<li><p>Sam stopped <code>Cabal</code> from writing <code>setup-config</code> twice
(<a href="https://github.com/haskell/cabal/pull/11949"><span>#11949</span></a>), fixing access-denied
failures (<a href="https://github.com/haskell/cabal/issues/11619"><span>#11619</span></a>).</p></li>
<li><p>Sam made <code>cabal-install</code> fall back to the external setup method on a
Cabal-library mismatch (<a href="https://github.com/haskell/cabal/pull/11950"><span>#11950</span></a>),
fixing stuck rebuilds of <code>build-type: Custom</code> packages
(<a href="https://github.com/haskell/cabal/issues/11942"><span>#11942</span></a>).</p></li>
<li><p>Sam allowed preserving the local build artifacts across minor Cabal version
bumps without causing recompilation or errors
(<a href="https://github.com/haskell/cabal/pull/12289"><span>#12289</span></a>).</p></li>
<li><p>Tobias switched pre-processor recompilation checks to use file monitoring
(<a href="https://github.com/haskell/cabal/pull/12021"><span>#12021</span></a>, with a preparatory
refactoring in <a href="https://github.com/haskell/cabal/pull/12065"><span>#12065</span></a>).
This fixes an issue with build tools not being re-run when their version
changes (<a href="https://github.com/haskell/cabal/issues/11411"><span>#11411</span></a>).</p></li>
</ul>
<h2 id="haskell-language-server">Haskell Language Server</h2>
<ul>
<li><p>Andrea added whole-project loading
(<a href="https://github.com/haskell/haskell-language-server/pull/5002"><span>#5002</span></a>),
as described in his <a href="https://well-typed.com/blog/2026/09/whole-project-loading-for-haskell-ide-tooling/">recent blog post</a>.
This builds on the groundwork Andrea laid in <code>hie-bios</code>: new load modes for
upfront loading of a cradle or project
(<a href="https://github.com/haskell/hie-bios/pull/508"><span>#508</span></a>) and the
<code>componentsToLoad</code> field
(<a href="https://github.com/haskell/hie-bios/pull/515"><span>#515</span></a>), both reviewed and
merged by Hannes.</p></li>
<li><p>Hannes and Zubin pushed <a href="https://github.com/guibou">Guibou</a>’s module-to-filename resolution over the finish line
and applied further optimisations to put HLS’s import resolution on par with GHC’s
(<a href="https://github.com/haskell/haskell-language-server/pull/4600"><span>#4600</span></a>).</p></li>
<li><p>Zubin made HLS unload linkables when any transitive dependency changes
(<a href="https://github.com/haskell/haskell-language-server/pull/5035"><span>#5035</span></a>),
and stopped HLS unloading all bytecode whenever new bytecode is generated
(<a href="https://github.com/haskell/haskell-language-server/pull/5033"><span>#5033</span></a>).</p></li>
<li><p>Hannes set the <code>FileCache</code> hook before typechecking
(<a href="https://github.com/haskell/haskell-language-server/pull/5030"><span>#5030</span></a>),
fixing <a href="https://github.com/haskell/haskell-language-server/issues/4675"><span>#4675</span></a>.</p></li>
<li><p>Hannes ensured the <code>DocMap</code> is never computed with stale data
(<a href="https://github.com/haskell/haskell-language-server/pull/4936"><span>#4936</span></a>).</p></li>
<li><p>Hannes tracked down multi-component session failures when expanding response
files (<a href="https://github.com/haskell/haskell-language-server/issues/5004"><span>#5004</span></a>),
fixed by whole-project loading.</p></li>
<li><p>Hannes substantially improved HLS’s error messages
(<a href="https://github.com/haskell/haskell-language-server/pull/5029"><span>#5029</span></a>).</p></li>
</ul>
<h3 id="google-summer-of-code-projects">Google Summer of Code projects</h3>
<ul>
<li><p>Magnus, Hannes and Andreas mentored the <a href="https://gist.github.com/Aster89/69a57c364ec2d0cc89f474cc3d194371">2026 GSoC project by Enrico Maria De Angelis</a>,
adding back the case-split plugin to HLS (<a href="https://github.com/haskell/haskell-language-server/pull/5014"><span>#5014</span></a>).</p></li>
<li><p>Zubin and Hannes mentored the <a href="https://gist.github.com/vidit-od/032f03afac51d0e3507b2b9d1bd9a277">2026 GSoC project by Vidit Odedra</a>,
adding go-to-definition for third-party dependencies to HLS (<a href="https://github.com/haskell/haskell-language-server/pull/4973"><span>#4973</span></a>, <a href="https://github.com/haskell/haskell-language-server/pull/5025"><span>#5025</span></a>).</p></li>
</ul>
<h3 id="haskell-syntax-highlighting">Haskell syntax highlighting</h3>
<ul>
<li>Hannes transferred the VS Code syntax highlighting extension to the
Haskell community (<a href="https://github.com/haskell/vscode-haskell-syntax-highlighting/pull/256"><span>#256</span></a>),
fixing a mishap that had caused
the extension to disappear from the VS Code marketplace, breaking syntax
highlighting for many users.</li>
</ul>
<h2 id="runtime-observability">Runtime observability</h2>
<h3 id="haskell-debugger">Haskell Debugger</h3>
<p>Rodrigo continued to lead development of the
<a href="https://github.com/well-typed/haskell-debugger">Haskell Debugger</a> (<code>hdb</code>), an
interactive step-through debugger for Haskell, with contributions from Andrea
and Hannes. It also gained a logo, designed by George Thomas (<a href="https://github.com/well-typed/haskell-debugger/pull/326"><span>#326</span></a>).</p>
<ul>
<li><p>Hannes and Rodrigo added support for the future release of GHC 10.2 (<a href="https://github.com/well-typed/haskell-debugger/pull/332"><span>#332</span></a>, <a href="https://github.com/well-typed/haskell-debugger/pull/359"><span>#359</span></a>).</p></li>
<li><p><code>hdb</code> has two mechanisms for listing threads, decoding stacks and collecting
exception information: external compiler commands where GHC supports them,
and a fallback that inspects the heap directly on older GHCs. Rodrigo
separated the two into distinct modules selected by a Cabal version
conditional, rather than interleaving them (<a href="https://github.com/well-typed/haskell-debugger/pull/356"><span>#356</span></a>).</p></li>
<li><p>Rodrigo made the <code>singleThread</code> flag of a <em>continue</em> request optional, as
the Debug Adapter Protocol specifies (<a href="https://github.com/haskell-debugger/dap/pull/38"><span>#38</span></a>).</p></li>
<li><p>Hannes made the stack frame decoding logic use the source locations that
<code>StackAnnotation</code> carries from <code>ghc-internal-9.1402</code> onwards
(<a href="https://github.com/well-typed/haskell-debugger/pull/333"><span>#333</span></a>).</p></li>
<li><p>Andrea made each stack frame report the variables bound in its own scope
(<a href="https://github.com/well-typed/haskell-debugger/pull/355"><span>#355</span></a>,
<a href="https://github.com/well-typed/haskell-debugger/issues/160"><span>#160</span></a>).</p></li>
</ul>
<h3 id="live-monitoring">Live monitoring</h3>
<p>Wen continued to develop
<a href="https://github.com/well-typed/eventlog-live/"><code>eventlog-live</code></a>, which allows
GHC’s eventlog to be monitored as a program runs, streaming telemetry data
from the runtime system to an external observability platform.
One focus was on improving the OpenTelemetry integration.</p>
<p>Releases 0.6.0.0 through 0.9.0.0 came out this quarter. See the
<a href="https://hackage.haskell.org/package/eventlog-live/changelog"><code>eventlog-live</code> changelog</a>
for more details.</p>
<h3 id="stack-and-ipe-profiling">Stack and IPE profiling</h3>
<p>Hannes and Wen worked on packages supporting stack profiling functionality, with
a release of <code>ghc-stack-profiler</code> 0.4.0.0 (<a href="https://hackage.haskell.org/package/ghc-stack-profiler/changelog">changelog</a>) and <code>ghc-stack-annotations</code> 0.2.0.0
(<a href="https://hackage.haskell.org/package/ghc-stack-annotations/changelog">changelog</a>).</p>
<p>Wen rewrote <code>ipedb</code>’s LSM-tree database API
(<a href="https://github.com/well-typed/ipedb/pull/18"><span>#18</span></a>) and released 0.2.0.0/0.2.0.1
with other improvements (see the <a href="https://hackage.haskell.org/package/ipedb/changelog">changelog</a>).</p>
<!-- References -->
]]></content:encoded>
</item>
<item>
    <title>Well-Typed at ZuriHac 2026</title>
    <link>https://well-typed.com/blog/2026/09/zurihac-well-typed-2026</link>
    <description><![CDATA[Well-Typed was strongly represented at this year's ZuriHac, with our team of Haskell experts giving
six talks across ZuriHac itself and the Haskell Ecosystem and Implementors' Workshops. If you couldn't make it to ZuriHac, the recordings are now available. In addition, we are delighted that
Dominik will [...]]]></description>
    <dc:creator>hannes, adam</dc:creator>
    <category>community</category>
    <category>ghc</category>
    <category>open-source</category>
    <category>talks</category>
    <category>training</category>
    <category>tutorial</category>
    <category>youtube</category>
    <category>zurihac</category>
    <guid isPermaLink="false">http://www.well-typed.com/blog/2026/09/zurihac-well-typed-2026</guid>
    <pubDate>Thu, 10 Sep 2026 00:00:00 GMT</pubDate>
    <content:encoded><![CDATA[<p>Well-Typed was strongly represented at this year’s <a href="https://zfoh.ch/zurihac2026/">ZuriHac</a>, with our team of Haskell experts giving
six talks across ZuriHac itself and the Haskell Ecosystem and Implementors’ Workshops. If you couldn’t make it to ZuriHac, the recordings are now available. In addition, we are delighted that
<a href="https://well-typed.com/people/dominik">Dominik</a> will be giving a keynote talk at <a href="https://munihac.de/2026.html">MuniHac 2026</a>
on 11th October, and there’s still time to register for MuniHac!</p>
<h3 id="zurihac-advanced-track">ZuriHac Advanced Track</h3>
<!-- more -->
<p><a href="https://well-typed.com/people/andres">Andres</a> and <a href="https://well-typed.com/people/andrea">Andrea</a> hosted the Advanced Track at ZuriHac, delivering two one-hour presentations on implementing dependently typed languages in Haskell, with a focus on dependent pattern matching.
They provide a good introduction to the world of dependent types as well as an in-depth discussion for language implementors.</p>
<div style="width: 560px; height: 315px" data-service="youtube" data-id="E5Hhlw1GdeU" data-thumbnail="../../../../img/youtube-thumbnails/zurihac26-dependently-typed-pattern-matching-workshop-part-1.jpg" data-autoscale>

</div>
<div style="width: 560px; height: 315px" data-service="youtube" data-id="Lvk7zLqkGwM" data-thumbnail="../../../../img/youtube-thumbnails/zurihac26-dependently-typed-pattern-matching-workshop-part-2.jpg" data-autoscale>

</div>
<h3 id="haskell-ecosystem-workshop">Haskell Ecosystem Workshop</h3>
<p><a href="https://well-typed.com/people/andreask">Andreas</a> gave an excellent talk on how to contribute to the Haskell ecosystem and why companies in particular should want to do so.</p>
<div style="width: 560px; height: 315px" data-service="youtube" data-id="y-NanV7A7f8" data-thumbnail="../../../../img/youtube-thumbnails/zurihac26-andreas-why-you-should-want.jpg" data-autoscale>

</div>
<h3 id="haskell-implementors-workshop">Haskell Implementors Workshop</h3>
<p>The Haskell Implementors Workshop was a great opportunity to share our progress on
improvements to GHC over the last year. It’s always nice to take a moment to reflect
on the progress we’ve made and the work we’ve done.</p>
<p><a href="https://well-typed.com/people/andreask">Andreas</a> kicked things off with the annual GHC status report. This report
provides a summary of the essential maintenance and community stewardship work that
Well-Typed performs for the GHC project.</p>
<div style="width: 560px; height: 315px" data-service="youtube" data-id="6bDQwW5GlnU" data-thumbnail="../../../../img/youtube-thumbnails/zurihac26-andreas-ghc-status-report.jpg" data-autoscale>

</div>
<p>Next, <a href="https://well-typed.com/people/hannes">Hannes</a> introduced a prototype implementation of <a href="https://github.com/well-typed/ghc-stack-profiler"><code>ghc-stack-profiler</code></a>, a lightweight profiler that allows you to profile your application without compiling with <code>-prof</code>.
The profiler uses source locations from <code>-finfo-table-map</code> for human readable profiles and can be used with GHC 9.10 and onwards.
Expect an in-depth post about <code>ghc-stack-profiler</code> soon!
You can also read more about our work in this area in posts tagged with <a href="https://well-typed.com/blog/tags/profiling">profiling</a>.</p>
<div style="width: 560px; height: 315px" data-service="youtube" data-id="13J-eIlLods" data-thumbnail="../../../../img/youtube-thumbnails/zurihac26-hannes-lightweight-profiler.jpg" data-autoscale>

</div>
<p>Finally, <a href="https://well-typed.com/people/rodrigo">Rodrigo</a> presented his progress on the <a href="https://github.com/well-typed/haskell-debugger"><code>haskell-debugger</code></a>, a standalone step-through debugger for GHC.
Since last year’s first introduction, many new features have been implemented, such as support for backtraces with source locations, custom debug visualisations of values and using the external interpreter for debugging multi-threaded applications.</p>
<div style="width: 560px; height: 315px" data-service="youtube" data-id="TCohAv2weMo" data-thumbnail="../../../../img/youtube-thumbnails/zurihac26-rodrigo-evolving-the-haskell-debugger.jpg" data-autoscale>

</div>
<h2 id="conclusion">Conclusion</h2>
<p>Well-Typed offer <a href="https://well-typed.com/ecosystem">Haskell Ecosystem Support Packages</a>
in partnership with the Haskell Foundation, to provide commercial
users with support from Well-Typed’s experts, while investing in the Haskell
community and its technical ecosystem.
These projects were made possible by funding from our clients, notably <a href="https://www.mercury.com/">Mercury</a>, who
are improving the experience for Haskell developers by supporting foundational work on Haskell tools.</p>
<p>It was great to meet everyone who attended the workshops and asked interesting
questions during and after the talks. We hope to see you all again at MuniHac,
or at next year’s ZuriHac!</p>
]]></content:encoded>
</item>
<item>
    <title>Whole project loading for Haskell IDE tooling</title>
    <link>https://well-typed.com/blog/2026/09/whole-project-loading-for-haskell-ide-tooling</link>
    <description><![CDATA[From Haskell Language Server 2.15.0.0 onwards, users can opt-in to loading all
components of their current project up front, rather than as needed. Doing so
ensures information about every module is fully available from the start, and
avoids pauses to re-initialize the session when opening a file from [...]]]></description>
    <dc:creator>andrea</dc:creator>
    <category>well-typed</category>
    <category>hls</category>
    <category>open-source</category>
    <category>coding</category>
    <category>build-systems</category>
    <guid isPermaLink="false">http://www.well-typed.com/blog/2026/09/whole-project-loading-for-haskell-ide-tooling</guid>
    <pubDate>Fri, 04 Sep 2026 00:00:00 GMT</pubDate>
    <content:encoded><![CDATA[<p>From <a href="https://github.com/haskell/haskell-language-server"><strong>Haskell Language Server</strong></a> <code>2.15.0.0</code> onwards, users can opt-in to loading all
components of their current project up front, rather than as needed. Doing so
ensures information about every module is fully available from the start, and
avoids pauses to re-initialize the session when opening a file from a new
component.</p>
<p>The feature is enabled by setting <code>componentsLoading</code> to <code>"multi: whole-project"</code> in the HLS configuration.</p>
<p>The next release of <a href="https://well-typed.github.io/haskell-debugger/"><strong>Haskell Debugger</strong></a> will load all components up front too.
Debug sessions will then be able to support breakpoints from any module in the
project, not just the entry point’s component.</p>
<p>The feature is provided by <code>hie-bios &gt;= 0.20</code> and the cradle config (<code>hie.yaml</code>)
can be used to fine-tune the list of components to load. The <a href="https://github.com/haskell/hie-bios#selecting-which-components-to-load">new
<code>componentsToLoad</code>
field</a>
lets you list the exact targets to pick, which are otherwise the <code>components</code>
listed in the cradle or inferred from the <code>cabal</code>/<code>stack</code> project.</p>
<!-- more -->
<h3 id="multi-component-sessions">Multi component sessions</h3>
<p>Haskell Language Server has supported multi component sessions for a while, e.g.
when working on a Cabal package with both a library and an executable their files
can all be loaded together, so jumping to definition from <code>app/Main.hs</code> can
land you in <code>lib/Foo.hs</code>.</p>
<p>However, so far each new component has been loaded into the session lazily, i.e.
only when files belonging to the component are opened. There are caches
involved, but each time this happens the GHC session is re-initialized from
scratch: the new set of components, and how to build them, is discovered by
querying the underlying build system, e.g. by invoking <code>cabal repl</code>/<code>stack repl</code>
and extracting the flags they would pass to <code>ghci</code>. Ultimately the new component
might have to go from being pre-compiled on disk to interpreted in-memory,
forcing all its descendants to be reconfigured too, so even if we wanted to be
more clever not much work could be saved.</p>
<p>This is not a big deal with just two components, but in a larger project it can be
surprising to have HLS behave differently in a given file according to which
other files have been opened. Also, when the session needs to be re-initialized
because of a new component, the induced pause can be a bit jarring.</p>
<h3 id="whole-project-loading">Whole project loading</h3>
<p>The new <em>whole project</em> mode instead loads all project components right away,
so that everything is available from the start. The downside is doing everything
up front means memory use and initialization time are also paid up front. Also,
a misconfigured component can stop the others from loading.</p>
<h4 id="in-haskell-language-server">In Haskell Language Server</h4>
<p>Eventually we hope to make whole project loading the default. For the moment,
starting with <code>haskell-language-server-2.15</code>, the new feature is enabled by
setting the new config field <code>componentsLoading</code> to <code>multi: whole-project</code>. The
other options are <code>multi: needed-only</code>, which is the default, and <code>single</code>, which
loads only a single component at once. The <code>componentsLoading</code> field replaces the
previous <code>sessionLoading</code> one which is now deprecated: <code>sessionLoading: "multipleComponents"</code>
is now <code>componentsLoading: "multi: needed-only"</code> and interpreted as such.</p>
<h4 id="in-haskell-debugger">In Haskell Debugger</h4>
<p>Haskell Debugger was actually the primary motivation for this work, as finding
out in the middle of a debug session that you cannot add a breakpoint in an
imported module is more dire than the issues with HLS.</p>
<p>Prior to whole-project loading, the typical setup where a package contains both
a library and an executable that depends on it would end up with only the
modules from the executable interpreted and so available to be stopped at.
The library was installed in a temporary package database and included only as a
dependency, so trying to set a breakpoint in a module from the library would just fail.</p>
<p>Haskell Debugger builds its session only once, as soon as it learns what the
entry point is, i.e. the module and function to start execution from. At this
point the debugger doesn’t know where the breakpoints will be, and so which
modules need to be interpreted. As new breakpoints can be set while execution
already started, waiting to know all of them to initialize the session is not an
option.</p>
<p>Because of the above we have gone ahead and made whole-project loading the
default and only way Haskell Debugger behaves, starting from the next release.</p>
<h4 id="how-it-works-and-further-configuration">How it works and further configuration</h4>
<p>What “all project components” means depends on the underlying cradle, either
specified in a <code>hie.yaml</code> file or inferred from what can be found in the root
directory. Recent enough <code>Cabal</code> and <code>Stack</code> are the two kinds of cradles that
actually support this: if the <code>hie.yaml</code> file specifies components we load
those, otherwise we ask the package manager to infer them. Inferring means
asking <code>Cabal</code> for target <code>all</code> while enabling tests and benchmarks, or asking
<code>Stack</code> for its default targets.</p>
<p>The above logic, like most of this new feature, is implemented by <code>hie-bios</code>
starting from version <code>0.20</code>. All details on how to fine-tune your cradle can be
found in <a href="https://github.com/haskell/hie-bios"><code>hie-bios</code>’s <code>README</code></a>, including
the new <a href="https://github.com/haskell/hie-bios#selecting-which-components-to-load"><code>componentsToLoad</code>
field</a>.</p>
<p>Before explaining the new field let’s go through a few examples with <code>Cabal</code> cradles to make
things more concrete. For each case we will list the <code>cabal repl</code> invocation
that <code>hie-bios</code> uses to fetch the correct flags.</p>
<p>A very minimal cradle (<code>hie.yaml</code>) will let <code>cabal</code> infer the components by
using the target <code>all</code>, i.e.</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode yaml"><code class="sourceCode yaml"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="fu">cradle</span><span class="kw">:</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="at">  </span><span class="fu">cabal</span><span class="kw">:</span></span></code></pre></div>
<p>will result in <code>cabal repl --enable-multi-repl --enable-tests --enable-benchmarks all</code>. The <code>--enable-*</code> flags are included so that the
session will cover everything in the project.</p>
<p>The <code>--enable-*</code> flags will override what specified in a <code>cabal.project</code> file,
so if the cradle includes one we omit them, i.e.</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode yaml"><code class="sourceCode yaml"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="fu">cradle</span><span class="kw">:</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="at">  </span><span class="fu">cabal</span><span class="kw">:</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a><span class="at">    </span><span class="fu">cabalProject</span><span class="kw">:</span><span class="at"> </span><span class="st">&quot;./cabal.project&quot;</span></span></code></pre></div>
<p>will result in <code>cabal repl --enable-multi-repl --project-file=./cabal.project all</code>.</p>
<p>If <code>components</code> are specified then those are the targets used, e.g.</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode yaml"><code class="sourceCode yaml"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="fu">cradle</span><span class="kw">:</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a><span class="at">  </span><span class="fu">cabal</span><span class="kw">:</span></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a><span class="at">    </span><span class="fu">components</span><span class="kw">:</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a><span class="at">      </span><span class="kw">-</span><span class="at"> </span><span class="kw">{</span><span class="fu">&quot;path&quot;</span><span class="kw">:</span><span class="at"> </span><span class="st">&quot;./src&quot;</span><span class="kw">,</span><span class="at"> </span><span class="fu">&quot;component&quot;</span><span class="kw">:</span><span class="at"> </span><span class="st">&quot;lib:foo&quot;</span><span class="kw">}</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a><span class="at">      </span><span class="kw">-</span><span class="at"> </span><span class="kw">{</span><span class="fu">&quot;path&quot;</span><span class="kw">:</span><span class="at"> </span><span class="st">&quot;./app&quot;</span><span class="kw">,</span><span class="at"> </span><span class="fu">&quot;component&quot;</span><span class="kw">:</span><span class="at"> </span><span class="st">&quot;exe:foo&quot;</span><span class="kw">}</span></span></code></pre></div>
<p>will result in <code>cabal repl --enable-multi-repl lib:foo exe:foo</code>.</p>
<p>Finally, if some component doesn’t play nice with <code>cabal repl</code> or the tooling that uses <code>hie-bios</code>,
the field <code>componentsToLoad</code> can be used to specify the exact list of targets to use, for example</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode yaml"><code class="sourceCode yaml"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="fu">cradle</span><span class="kw">:</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="at">  </span><span class="fu">cabal</span><span class="kw">:</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a><span class="at">    </span><span class="fu">componentsToLoad</span><span class="kw">:</span><span class="at"> </span><span class="kw">[</span><span class="st">&quot;lib:foo&quot;</span><span class="kw">,</span><span class="st">&quot;lib:bar&quot;</span><span class="kw">]</span></span></code></pre></div>
<p>will result in <code>cabal repl --enable-multi-repl lib:foo lib:bar</code>.</p>
<h3 id="conclusion">Conclusion</h3>
<p>Hopefully whole-project loading will provide a less surprising and smoother dev
experience on projects with multiple components. It’s been my new default and
working well for me since it got merged. Feedback is welcome on the <a href="https://github.com/haskell/haskell-language-server/issues">HLS
issue tracker</a>.
<a href="https://github.com/haskell/haskell-language-server/issues/5005">Issue #5005</a> is the
issue about the feature introduction.</p>
<p>This work has been performed in collaboration with Mercury, who have a long-term
commitment to the scalability and robustness of the Haskell ecosystem.
Well-Typed are always interested in projects and looking for funding to improve
HLS and other Haskell tools. Please contact <a href="mailto:info@well-typed.com">info@well-typed.com</a> if we might be
able to work with you!</p>
]]></content:encoded>
</item>
<item>
    <title>New falsify release</title>
    <link>https://well-typed.com/blog/2026/07/falsify-4</link>
    <description><![CDATA[Recently Martijn Bastiaan, QBayLogic's COO,
invited Well-Typed to come to QBayLogic HQ and give a one-day workshop on
falsify, the new property based
testing library that I developed for the Haskell Symposium back in 2023
(paper,
presentation).
QBayLogic is the company behind
Clash: a purely functional [...]]]></description>
    <dc:creator>edsko</dc:creator>
    <category>coding</category>
    <category>open-source</category>
    <category>testing</category>
    <guid isPermaLink="false">http://www.well-typed.com/blog/2026/07/falsify-4</guid>
    <pubDate>Fri, 17 Jul 2026 00:00:00 GMT</pubDate>
    <content:encoded><![CDATA[<p>Recently Martijn Bastiaan, QBayLogic’s <a href="https://qbaylogic.com/team/">COO</a>,
invited Well-Typed to come to QBayLogic HQ and give a one-day workshop on
<a href="https://hackage.haskell.org/package/falsify">falsify</a>, the new property based
testing library that I developed for the Haskell Symposium back in 2023
(<a href="https://dl.acm.org/doi/10.1145/3609026.3609733">paper</a>,
<a href="https://www.youtube.com/watch?v=csKkTas6X58">presentation</a>).
<a href="https://qbaylogic.com/">QBayLogic</a> is the company behind
<a href="https://clash-lang.org/">Clash</a>: a purely functional language for hardware
design. In case you haven’t heard of it, Clash translates Haskell to VHDL or
Verilog, which can in turn be translated to actual hardware. It literally uses
<a href="https://hackage.haskell.org/package/clash-ghc"><code>ghc</code> as its frontend</a> so you
have essentially the full power of Haskell available. It’s a great project, I
recommend checking it out.</p>
<p>The <code>falsify</code> library takes its main inspiration from the Python
<a href="https://hypothesis.works/">Hypothesis</a> library, though it is not a direct
translation: it takes the same core idea (“parse, don’t generate”) but
reinterprets it in a way that better suits the Haskell way of thinking: more
axiomatic approach, support for generating infinite data types (including
functions), etc. For more information on <code>falsify</code>, see also the original blog
post that announced it, <a href="https://well-typed.com/blog/2023/04/falsify/"><code>falsify</code>: Hypothesis-inspired shrinking for
Haskell</a>.</p>
<p>At QBayLogic HQ we spent the morning developing
<a href="https://github.com/well-typed/mini-falsify"><code>mini-falsify</code></a> from scratch, so
that we could focus on the main ideas without getting bogged down in the details
of the full library; similar in spirit to for example
<a href="https://www.well-typed.com/blog/2015/11/implementing-a-minimal-version-of-haskell-servant/"><code>TinyServant</code></a>,
or perhaps Stephen Diehl’s <a href="https://sdiehl.github.io/typechecker-zoo/">Typechecker
Zoo</a>. In the afternoon we hacked on
<code>falsify</code> and its application within the <code>clash</code> ecosystem. Partly as a result
of that work and partly as a result of me taking this opportunity to do some
long overdue maintenance on <code>falsify</code>, there is now a new falsify release:
<a href="https://hackage.haskell.org/package/falsify-0.4.0"><code>falsify-0.4.0</code></a>. In the
remainder of this blog post we give a brief overview of the main changes.</p>
<!-- more -->
<h2 id="new-feature-context">New feature: <code>Context</code></h2>
<p>Most of this <code>falsify</code> release is just cleanup, but there is one important new
feature, spear-headed by Peter Lebbing and Martijn Bastiaan from QBayLogic. The
<code>Property</code> monad now has an important new function, called
<a href="https://hackage-content.haskell.org/package/falsify-0.4.0/docs/Test-Falsify.html#v:getContext"><code>getContext</code></a>:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="ot">getContext ::</span> <span class="dt">Property</span> <span class="dt">Context</span></span></code></pre></div>
<p>The most important information that the <em>context</em> of a property provides is
how many tests we are running for each property (how hard are we trying to
falsify this property), and which iteration this <em>particular</em> attempt is.</p>
<p>This can be quite useful, for example when you want to start by looking at small
test cases and then slowly broaden the scope. Just as a trivial example,
consider the property that “no number is equal to 5”. If we use
<a href="https://hackage-content.haskell.org/package/falsify-0.4.0/docs/Test-Falsify-Generator.html#v:prim"><code>prim</code></a>
to generate the number to test, producing an arbitrary <code>Word64</code>, the chances
that we will find the <em>one</em> counter-example (5) in that enormous search space
are essentially non-existent:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="ot">demo ::</span> <span class="dt">Property</span> ()</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>demo <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>    x <span class="ot">&lt;-</span> gen Gen.prim</span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a>    assert <span class="op">$</span> P.ne <span class="op">.$</span> (<span class="st">&quot;forbidden&quot;</span>, <span class="dv">5</span>)</span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a>                  <span class="op">.$</span> (<span class="st">&quot;x&quot;</span>,<span class="ot"> x ::</span> <span class="dt">Word64</span>)</span></code></pre></div>
<p>However, we could start with a small range and slowly grow that range; <code>falsify</code>
now also offers a convenience function, defined in terms of <code>getContext</code>, for
this specific purpose:
<a href="https://hackage-content.haskell.org/package/falsify-0.4.0/docs/Test-Falsify.html#v:sized"><code>sized</code></a>,
so-named because it is somewhat similar in spirit to <a href="https://hackage-content.haskell.org/package/QuickCheck-2.18.0.0/docs/Test-QuickCheck-Gen.html#v:sized"><code>sized</code> in
<code>QuickCheck</code></a>:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="ot">sized ::</span> <span class="kw">forall</span> e a<span class="op">.</span> (<span class="dt">ProperFraction</span> <span class="ot">-&gt;</span> a) <span class="ot">-&gt;</span> <span class="dt">Property</span> a</span></code></pre></div>
<p>A
<a href="https://hackage-content.haskell.org/package/falsify-0.4.0/docs/Data-Falsify-ProperFraction.html#t:ProperFraction"><code>ProperFraction</code></a>
is in the half-open interval [0,1); that is, between 0 (inclusive) and 1
(exclusive). We can use this to refine our demo property:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="ot">demo2b ::</span> <span class="dt">Property</span> ()</span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a>demo2b <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a>    l <span class="ot">&lt;-</span> sized <span class="op">$</span> ProperFraction.scaleIntegral <span class="dv">100</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a>    x <span class="ot">&lt;-</span> gen <span class="op">$</span> Gen.inRange <span class="op">$</span> Range.inclusive (<span class="dv">0</span>, l)</span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a>    assert <span class="op">$</span> P.ne <span class="op">.$</span> (<span class="st">&quot;forbidden&quot;</span>, <span class="dv">5</span>)</span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a>                  <span class="op">.$</span> (<span class="st">&quot;x&quot;</span>,<span class="ot"> x ::</span> <span class="dt">Word64</span>)</span></code></pre></div>
<p>This property is easily falsified.</p>
<p>As an aside, I would be somewhat cautious in using this approach. While it is
sometimes unavoidable, in general I would recommend generating test case of
arbitrary size and then shrinking them down; this is often more likely to
actually find counter-examples. If there are specific edge cases that you want
to hit, write a generator that covers those edge cases specifically, and perhaps
use labelling
(<a href="https://hackage-content.haskell.org/package/falsify-0.4.0/docs/Test-Falsify.html#v:label"><code>label</code></a>
and co) to check that those edge cases are indeed covered. However, as this demo
shows, for <em>some</em> properties explicitly searching for small domains first can be
very helpful.</p>
<h2 id="cleanup">Cleanup</h2>
<p>The most important change in this release is a cleanup of the code base:</p>
<ul>
<li>There is now a separate <a href="https://hackage.haskell.org/package/tasty-falsify-0.1.0"><code>tasty-falsify</code></a>
package that provides integration with the <a href="https://hackage.haskell.org/package/tasty"><code>tasty</code></a> test framework; <code>falsify</code> itself now provides <a href="https://hackage-content.haskell.org/package/falsify-0.4.0/docs/Test-Falsify-Driver.html"><code>Test.Falsify.Driver</code></a>.</li>
<li>The module hierarchy has been significantly cleaned up. For example, there are now a bunch of new <code>Data.Falsify.*</code> modules for specific datatypes; previously some of these were exported by <code>Test.Falsify.Gen</code> instead; now that module only contains the <em>generators</em> for those datatypes.</li>
<li>Deprecated functions have been removed</li>
<li>Some functions have been renamed and some <code>type</code> aliases have been replaced by <code>newtype</code> definitions for increased API clarity.</li>
<li>All Haddock warnings have been addressed.</li>
</ul>
<p>For a full list of changes, please refer to the <a href="https://hackage.haskell.org/package/falsify-0.4.0/changelog">changelog</a>.</p>
<h2 id="conclusions">Conclusions</h2>
<p>Shrinking can be handled manually, in the style of
<a href="https://hackage.haskell.org/package/QuickCheck"><code>QuickCheck</code></a>; or
automatically, in the style of
<a href="https://hackage.haskell.org/package/hedgehog"><code>hedgehog</code></a> or in the style of
<a href="https://hackage.haskell.org/package/falsify"><code>falsify</code></a>. If you are willing to
put effort into writing good shrinkers for all your types, then QuickCheck is
still your best bet.</p>
<p>If you consider the cost of manually writing shrinkers too large, then within
the Haskell ecosystem you have a choice between <code>hedgehog</code> and <code>falsify</code>. The
former has the considerable advantage that it’s been around for quite a long
time and is a very polished library. The downside is that every time you use
monadic bind you introduce a cut-point, which can often result in poor quality
shrinking. With <a href="https://hypothesis.works/"><code>Hypothesis</code></a> showing the way,
<code>falsify</code> solves that cut-point problem, though even with <code>falsify</code> it still
matters how you write your generators: shrinking is <em>never</em> truly free.
Moreover, <code>falsify</code> should be considered an experimental library: it’s nowhere
near as battle-tested as Hedgehog, never mind QuickCheck. That said, thanks to
the interest of QBayLogic <code>falsify</code> is now a little more mature, so thank you
QBayLogic!</p>
<p>For clients who are looking for professional support for the use of <code>falsify</code>,
or indeed any other Haskell library, don’t hesitate to contact us at
<a href="mailto:info@well-typed.com">info@well-typed.com</a>.</p>
]]></content:encoded>
</item>
<item>
    <title>Haskell ecosystem activities report: March–May 2026</title>
    <link>https://well-typed.com/blog/2026/06/haskell-ecosystem-report-march-may-2026</link>
    <description><![CDATA[This is the thirty-first edition of our Haskell ecosystem activities report,
which describes the work Well-Typed are doing on GHC, Cabal, HLS and other parts
of the core Haskell toolchain. The current edition covers roughly the months of
March 2026 to May 2026.

You can find the previous editions collected [...]]]></description>
    <dc:creator>adam, andrea, andreask, davide, duncan, hannes, magnus, mikolaj, rodrigo, sam, tobias, wen, wolfgang, zubin</dc:creator>
    <category>well-typed</category>
    <category>ghc</category>
    <category>community</category>
    <category>haskell-ecosystem-report</category>
    <guid isPermaLink="false">http://www.well-typed.com/blog/2026/06/haskell-ecosystem-report-march-may-2026</guid>
    <pubDate>Fri, 19 Jun 2026 00:00:00 GMT</pubDate>
    <content:encoded><![CDATA[<p>This is the thirty-first edition of our Haskell ecosystem activities report,
which describes the work Well-Typed are doing on GHC, Cabal, HLS and other parts
of the core Haskell toolchain. The current edition covers roughly the months of
March 2026 to May 2026.</p>
<p>You can find the previous editions collected under the
<a href="https://well-typed.com/blog/tags/haskell-ecosystem-report">haskell-ecosystem-report tag</a>.</p>
<h2 id="sponsorship">Sponsorship</h2>
<p>We offer <a href="https://well-typed.com/ecosystem/">Haskell Ecosystem Support Packages</a> to provide commercial
users with support from Well-Typed’s experts while investing in the Haskell
community and its technical ecosystem including through the work described in
this report. To find out more, read our <a href="https://well-typed.com/blog/2025/06/haskell-ecosystem-support-packages">announcement of these
packages</a> in partnership with
the Haskell Foundation. We need funding to continue this essential maintenance work!</p>
<p>Many thanks to our Haskell Ecosystem Supporters: <a href="https://www.sc.com/">Standard Chartered</a>,
<a href="https://www.channable.com/">Channable</a> and <a href="https://qbaylogic.com/">QBayLogic</a>,
as well as to our other clients who also contribute to making this work possible:
<a href="https://www.anduril.com/">Anduril</a>, <a href="https://juspay.in/">Juspay</a> and <a href="https://mercury.com/">Mercury</a>;
and to the <a href="https://opencollective.com/haskell-language-server">HLS Open Collective</a> for
supporting HLS release management.</p>
<h2 id="highlights">Highlights</h2>
<!-- more -->
<h3 id="reinstallable-base">Reinstallable base</h3>
<p><a href="https://well-typed.com/people/rodrigo">Rodrigo</a> and <a href="https://well-typed.com/people/wolfgang">Wolfgang</a> have continued working to disentangle <code>base</code> from GHC,
with the ultimate goal of being able to use different versions of <code>base</code> with
a single GHC version. This is all part of a long-term project to make
GHC upgrades easier, documented in the
<a href="https://github.com/well-typed/reinstallable-base">reinstallable-base repo</a>,
and recently described in an
<a href="https://blog.haskell.org/making-ghc-upgrades-easy/">excellent haskell.org blog article by Simon Peyton Jones</a>.
Simon, <a href="https://well-typed.com/people/rodrigo">Rodrigo</a> and <a href="https://well-typed.com/people/wolfgang">Wolfgang</a> are working together to rethink GHC’s treatment of
“known-key names” so <code>base</code> will be much less tightly coupled to <code>ghc-internal</code> (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15899">!15899</a>).</p>
<p>One big milestone was making the current <code>base</code> buildable both with GHC 9.14
(<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16070">!16070</a>) and with the upcoming GHC 10.0 (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16035">!16035</a>).</p>
<p>Various other pieces of technical groundwork were required:</p>
<ul>
<li>moving most of the <code>System.IO</code> implementation into <code>base</code> (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15694">!15694</a>),</li>
<li>similarly moving <code>Text.Read</code> (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15693">!15693</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15777">!15777</a>),</li>
<li>making various SafeHaskell-related changes (<a href="https://github.com/haskell/core-libraries-committee/issues/408">CLC <span>#408</span></a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16034">!16034</a>).</li>
</ul>
<p><a href="https://well-typed.com/people/wolfgang">Wolfgang</a> also opened several CLC proposals identifying key areas that require further
work:</p>
<ul>
<li>the deprecation of unstable <code>base</code> modules (<a href="https://github.com/haskell/core-libraries-committee/issues/392">CLC <span>#392</span></a>), and</li>
<li>working towards making <code>bytestring</code> and <code>text</code> not depend on internal
I/O machinery (<a href="https://github.com/haskell/core-libraries-committee/issues/410">CLC <span>#410</span></a>).</li>
</ul>
<h3 id="semaphores-and--jsem">Semaphores and <code>-jsem</code></h3>
<p>The implementation of semaphores used by <code>cabal-install</code> and GHC to coordinate
concurrency suffered from a serious issue when GHC and <code>cabal-install</code> were
linked against different libc implementations (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/25087">#25087</a>).</p>
<p><a href="https://well-typed.com/people/zubin">Zubin</a> remedied this issue with a new semaphore implementation on top
of Unix domain sockets (<a href="https://gitlab.haskell.org/ghc/semaphore-compat/-/merge_requests/8"><span>!8</span></a>), described in <a href="https://github.com/ghc-proposals/ghc-proposals/pull/673">GHC proposal <span>#673</span></a>. GHC (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15729"><span>!15729</span></a>) and
<code>cabal-install</code> (<a href="https://github.com/haskell/cabal/pull/11628"><span>#11628</span></a>) were then
switched to this new protocol version.</p>
<p>As part of this work, <a href="https://well-typed.com/people/zubin">Zubin</a> also identified some deficiencies in the semantics
of interruptible FFI (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/27110">#27110</a>).</p>
<h3 id="cabal-modernisation">Cabal modernisation</h3>
<p><a href="https://well-typed.com/people/sam">Sam</a> worked to modernise the architecture of <code>cabal-install</code> by making it directly call the Cabal library to build packages (<a href="https://github.com/haskell/cabal/pull/11703"><span>#11703</span></a>).
This unlocked the performance optimisations described in <a href="https://well-typed.com/blog/2026/05/faster-haskell-builds/">a recent blog post</a> (<a href="https://github.com/haskell/cabal/pull/11767"><span>#11767</span></a>, <a href="https://github.com/haskell/cabal/pull/11768"><span>#11768</span></a>), achieving
speed-ups of around 10-15% in certain situations.</p>
<p>In the long term, we would like to move towards Cabal having a fine-grained build graph across the whole project, rather than using a component-level build graph and relying on GHC to orchestrate the compilation of all the modules in a component/package. This will unlock more parallelism and other developer experience improvements.</p>
<p><a href="https://well-typed.com/people/sam">Sam</a> also implemented many improvements to <code>build-type: Hooks</code>, the modern
replacement for <code>build-type: Custom</code>:</p>
<ul>
<li>an improved API for <code>SetupHooks</code> authors (<a href="https://github.com/haskell/cabal/pull/11772"><span>#11772</span></a>),</li>
<li>better documentation (<a href="https://github.com/haskell/cabal/pull/11771"><span>#11771</span></a>),</li>
<li>improved recompilation checking for pre-build rules (<a href="https://github.com/haskell/cabal/pull/11731"><span>#11731</span></a>),</li>
<li>support for generating non-Haskell files in pre-build rules (<a href="https://github.com/haskell/cabal/pull/11573"><span>#11573</span></a>).</li>
</ul>
<p><a href="https://well-typed.com/people/tobias">Tobias</a> chimed in by adding support for recursive globs in file monitoring, which is useful for <code>SetupHooks</code> (<a href="https://github.com/haskell/cabal/pull/11658"><span>#11658</span></a>).</p>
<h3 id="windows-dll-support">Windows DLL support</h3>
<p><a href="https://well-typed.com/people/duncan">Duncan</a>, with the help of <a href="https://well-typed.com/people/davide">David</a>, embarked on the project of adding proper dynamic
linking support to GHC on Windows. While this is still work-in-progress,
several important steps have already been taken:</p>
<ul>
<li>extending Cmm syntax to support symbols from external DSOs/DLLs (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15135">!15135</a>),</li>
<li>solving the recursive DLL dependency between the RTS and <code>ghc-internal</code>
(<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15907">!15907</a>),</li>
<li>making GHC use <code>__attribute__((dllimport))</code> for external symbol
declarations (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15914">!15914</a>),</li>
<li>updating Hadrian to create individual <code>.def</code> files per <code>ghc-internal</code> DLL
(<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16082">!16082</a>).</li>
</ul>
<h3 id="critical-process-issue-on-macos">Critical <code>process</code> issue on macOS</h3>
<p><a href="https://well-typed.com/people/magnus">Magnus</a> diagnosed a critical issue with <code>process</code> on macOS
(<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/27144">#27144</a>, <a href="https://github.com/haskell/process/issues/356"><span>#356</span></a>), implementing a fix
in <a href="https://github.com/haskell/process/pull/363"><span>#363</span></a>. As part of this work,
he discovered an issue with several uploaded versions of <code>process</code>
(<a href="https://github.com/haskell/process/issues/365"><span>#365</span></a>) which was resolved by
coordinating with the <code>process</code> maintainers.</p>
<h2 id="ghc">GHC</h2>
<h3 id="releases">Releases</h3>
<p>Keeping all of GHC’s active release branches in good shape is a demanding,
largely behind-the-scenes effort. Each release requires triaging and backporting
a steady stream of bugfixes, with patches that often don’t cleanly apply and
require careful reworking. Information about upcoming releases is available on the
<a href="https://gitlab.haskell.org/ghc/ghc/-/wikis/GHC-Status">GHC release status page</a>.</p>
<p>GHC currently has four (!) active branches. <a href="https://well-typed.com/people/zubin">Zubin</a> worked towards the
10.0 release as well as on 9.14.2; <a href="https://well-typed.com/people/magnus">Magnus</a> prepared 9.12.5, and <a href="https://well-typed.com/people/andreask">Andreas</a>
took on 9.10.4.</p>
<h3 id="frontend">Frontend</h3>
<ul>
<li><p><a href="https://well-typed.com/people/rodrigo">Rodrigo</a> bumped the default language edition to GHC2024 (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26039">#26039</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14420">!14420</a>).
Starting from GHC 10.0, code without an explicit language edition will
default to GHC2024. Compared to GHC2021, this adds the extensions <code>DataKinds</code>,
<code>DerivingStrategies</code>, <code>DisambiguateRecordFields</code>, <code>ExplicitNamespaces</code>,
<code>GADTs</code>, <code>MonoLocalBinds</code>, <code>LambdaCase</code> and <code>RoleAnnotations</code>.</p></li>
<li><p><a href="https://well-typed.com/people/magnus">Magnus</a> fixed the documentation of <code>getSizeofMutableByteArray#</code> (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16065">!16065</a>).</p></li>
<li><p><a href="https://well-typed.com/people/sam">Sam</a> fixed a loop with <code>-XDeepSubsumption</code> on GHC 9.14 (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26823">#26823</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15604">!15604</a>).</p></li>
<li><p><a href="https://well-typed.com/people/wolfgang">Wolfgang</a> made several performance improvements to the driver, allowing
downsweep to use existing module graph nodes (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16028">!16028</a>), introducing a cache
for hole modules (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15888">!15888</a>), and storing home unit dependencies as sets (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15849">!15849</a>).</p></li>
<li><p><a href="https://well-typed.com/people/hannes">Hannes</a> made several performance improvements to <code>ghc-pkg</code>, speeding up its closure
computation (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16062">!16062</a>) and migrating it to use <code>OsPath</code> (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15584">!15584</a>).</p></li>
<li><p><a href="https://well-typed.com/people/andreask">Andreas</a> modularised the <code>GHC.Driver.Main</code> module (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15940">!15940</a>), as that single
module had become quite unwieldy.</p></li>
<li><p>Following on from previous work allowing typechecker plugins to run in the
pattern match checker (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14797">!14797</a>), <a href="https://well-typed.com/people/sam">Sam</a> made sure that plugins are only initialised
once per module (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15485">!15485</a>), fixing the regression reported in <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26839">#26839</a>.</p></li>
<li><p><a href="https://well-typed.com/people/rodrigo">Rodrigo</a> provided a standalone reproducer for the simplifier slowness reported
in <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26989">#26989</a>, which allowed Simon Peyton Jones to diagnose and fix this quadratic
behaviour (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15725">!15725</a>).</p></li>
<li><p><a href="https://well-typed.com/people/sam">Sam</a> investigated the type family performance regressions reported in <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26426">#26426</a>,
identifying two key patches that had accidentally fixed the regression and
recording these findings in Notes (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16113">!16113</a>).</p></li>
<li><p><a href="https://well-typed.com/people/andreask">Andreas</a> identified that a patch to use <code>Generics</code> within GHC significantly
regressed compile times (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/27191">#27191</a>). The offending patch was reverted for the
time being, and the <a href="https://gitlab.haskell.org/ghc/ghc/-/wikis/commentary/coding-style#132-avoid-genericdata-instances">coding style</a> Wiki page was updated to
document this unfortunate limitation.</p></li>
</ul>
<h3 id="backend">Backend</h3>
<ul>
<li><p><a href="https://well-typed.com/people/sam">Sam</a> finished up work by Matthew Craven to improve the representation of
floating point literals in GHC, fixing several issues with the treatment of
negative zero and NaN values (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15528">!15528</a>). This brings GHC much closer to proper
IEEE 754 floating point compliance.</p></li>
<li><p><a href="https://well-typed.com/people/andreask">Andreas</a> discovered a bug with AArch64 profiled dynamic builds of GHC and
concluded that it was a bug in <code>gcc</code>/<code>binutils</code> (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26994">#26994</a>).</p></li>
<li><p><a href="https://well-typed.com/people/sam">Sam</a> fixed a crash in Core Prep caused by invalid profiling tick occurrences
(<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/27182">#27182</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16003">!16003</a>).</p></li>
<li><p><a href="https://well-typed.com/people/sam">Sam</a> investigated a crash in <code>mkDupableContWithDmds</code>, and worked with Simon
Peyton Jones to land a fix (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/27261">#27261</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16084">!16084</a>).</p></li>
<li><p><a href="https://well-typed.com/people/sam">Sam</a> fixed several bugs in the implementation of the <code>hsExprType</code> function
(<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26910">#26910</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15772">!15772</a>).</p></li>
<li><p><a href="https://well-typed.com/people/zubin">Zubin</a> fixed the typing of void pointer chains in <code>capi</code> FFI calls to avoid
<code>-Wincompatible-pointer-type</code> errors by the C compiler (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26852">#26852</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15547">!15547</a>).</p></li>
<li><p><a href="https://well-typed.com/people/sam">Sam</a> helped first-time contributor Avery Parker land his AmeriHac contribution
to add constant folding for SIMD vector operations (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15512">!15512</a>).</p></li>
<li><p><a href="https://well-typed.com/people/sam">Sam</a> reviewed several patches to the AArch64 native code generator by Ian Duncan,
in the process uncovering a cluster of latent correctness bugs:</p>
<ul>
<li><a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15620">!15620</a> fixed a bug in “shift right” using a logical shift instead of an
arithmetic shift (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26979">#26979</a>). A separate register clobbering bug was
identified and fixed.</li>
<li><a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15619">!15619</a> fixed sign extension at 32-bit width (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26978">#26978</a>). A separate bug
with the overflow bit was identified and fixed (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/27047">#27047</a>). Another
register clobbering bug was found and fixed separately (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/27046">#27046</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16031">!16031</a>).</li>
</ul></li>
<li><p><a href="https://well-typed.com/people/sam">Sam</a> fixed a recalcitrant “failed to detect OverLit” crash in
<code>coreExprToPmLit</code> (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15895">!15895</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/27124">#27124</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/25926">#25926</a>).</p></li>
<li><p><a href="https://well-typed.com/people/magnus">Magnus</a> and <a href="https://well-typed.com/people/sam">Sam</a> investigated the GHC panic reported in <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/27227">#27227</a>, which was
narrowed down to Cabal bug <a href="https://github.com/haskell/cabal/issues/7684"><span>#7684</span></a>,
and subsequently fixed (<a href="https://github.com/haskell/cabal/pull/11791"><span>#11791</span></a>).</p></li>
</ul>
<h3 id="ghci-bytecode">ghci &amp; bytecode</h3>
<p>Work continued on making the bytecode interpreter, which underpins GHCi as well
as the debugger, more capable and memory efficient.</p>
<ul>
<li><p><a href="https://well-typed.com/people/hannes">Hannes</a> introduced the <code>-fimport-loaded-target</code> ghci flag to fix a regression
with the <code>:add</code> command relating to support for multiple home units
(<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15533">!15533</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26866">#26866</a>).</p></li>
<li><p><a href="https://well-typed.com/people/hannes">Hannes</a> fixed a regression in the behaviour of <code>:load</code> (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/27202">#27202</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15980">!15980</a>).</p></li>
<li><p><a href="https://well-typed.com/people/hannes">Hannes</a> added support for <code>hpc</code> in the bytecode interpreter (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/27036">#27036</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15843">!15843</a>).</p></li>
<li><p><a href="https://well-typed.com/people/rodrigo">Rodrigo</a> added support for static constructors in the bytecode interpreter,
also fixing its treatment of unlifted values (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/25636">#25636</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15221">!15221</a>).</p></li>
<li><p><a href="https://well-typed.com/people/hannes">Hannes</a> improved the memory usage of the bytecode interpreter by recording only
a <code>LinkableUsage</code> instead of <code>Linkable</code> in <code>LoaderState</code> (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26500">#26500</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/27018">#27018</a>,
<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15689">!15689</a>).</p></li>
</ul>
<h3 id="rts">RTS</h3>
<ul>
<li><p><a href="https://well-typed.com/people/duncan">Duncan</a> consolidated the ticker implementations to a single one on POSIX
(<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15781">!15781</a>), removing signal-based ticker implementations (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15757">!15757</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16014">!16014</a>)
which presented a number of disadvantages (described in <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/27073">#27073</a>).</p></li>
<li><p><a href="https://well-typed.com/people/davide">David</a> resurrected an old MR improving the documentation of the allocator
(<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/3812">!3812</a>).</p></li>
<li><p><a href="https://well-typed.com/people/andreask">Andreas</a> added missing profiling headers for <code>origin_thunk</code> frames (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15665">!15665</a>).</p></li>
<li><p><a href="https://well-typed.com/people/duncan">Duncan</a> identified some incorrect STOP_THREAD status codes in <code>ghc-events</code>
(<a href="https://github.com/haskell/ghc-events/issues/130"><span>#130</span></a>).</p></li>
</ul>
<h3 id="build-system">Build system</h3>
<ul>
<li><p><a href="https://well-typed.com/people/davide">David</a> made several improvements to GHC’s build system and Hadrian, in
relation to his work on Windows DLL support:</p>
<ul>
<li>a fix for Unix paths in ACLOCAL_PATH (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16112">!16112</a>),</li>
<li>proper LLVM program versioning logic (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16025">!16025</a>),</li>
<li>work on response files to avoid MAX_PATH issues on Windows (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15891">!15891</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16120">!16120</a>).</li>
</ul></li>
<li><p><a href="https://well-typed.com/people/andreask">Andreas</a> fixed an issue with <code>--target</code> in the configure script (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15649">!15649</a>).</p></li>
<li><p><a href="https://well-typed.com/people/zubin">Zubin</a> stopped Hadrian from including package hashes in Haddock directory names,
as this was causing many broken links when browsing Haddocks (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26635">#26635</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15475">!15475</a>).</p></li>
</ul>
<h3 id="ghc-contribution-experience">GHC contribution experience</h3>
<ul>
<li><p><a href="https://well-typed.com/people/magnus">Magnus</a> overhauled the MR template to be more helpful for new contributors
(<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15943">!15943</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/27165">#27165</a>).</p></li>
<li><p>Many team members provided reviews and helped land contributions from others,
as described elsewhere in this report.</p></li>
</ul>
<h3 id="ci-and-tests">CI and tests</h3>
<ul>
<li><p><a href="https://well-typed.com/people/magnus">Magnus</a> has been doing significant work on the gitlab.haskell.org
instance and CI infrastructure to improve robustness of CI, deal with spam
and manage the server load imposed by AI crawlers.</p></li>
<li><p><a href="https://well-typed.com/people/magnus">Magnus</a> changed the Darwin CI to clone rather than copy when creating the Cabal cache (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15683">!15683</a>).</p></li>
<li><p><a href="https://well-typed.com/people/sam">Sam</a> added a vendored mini-QuickCheck component for use in GHC’s testsuite,
fixing some bugs in the previous implementation used in the <code>foundation</code>
primops testsuite (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15893">!15893</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/25969">#25969</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/25990">#25990</a>).</p></li>
</ul>
<h2 id="haskell-language-server">Haskell Language Server</h2>
<ul>
<li><p><a href="https://well-typed.com/people/zubin">Zubin</a> prepared release 2.14.0 of the Haskell Language Server (<a href="https://github.com/haskell/haskell-language-server/pull/4897"><span>#4897</span></a>).</p></li>
<li><p><a href="https://well-typed.com/people/hannes">Hannes</a> reviewed many HLS PRs from contributors:</p>
<ul>
<li><a href="https://github.com/haskell/haskell-language-server/pull/4958"><span>#4958</span></a> adding
a code action to delete unused bindings,</li>
<li><a href="https://github.com/haskell/haskell-language-server/pull/4913"><span>#4913</span></a> improving
the handling of <code>hs-boot</code> files,</li>
<li><a href="https://github.com/haskell/haskell-language-server/pull/4915"><span>#4915</span></a> updating
the splice plugin,</li>
<li><a href="https://github.com/haskell/haskell-language-server/pull/4917"><span>#4917</span></a> preserving
qualified field names when expanding record inlay hints,</li>
<li><a href="https://github.com/haskell/haskell-language-server/pull/4902"><span>#4902</span></a> excluding
internal superclass names in class placeholders,</li>
<li><a href="https://github.com/haskell/haskell-language-server/pull/4886"><span>#4886</span></a> cancelling
outstanding threads when closing the SQLite connection,</li>
<li><a href="https://github.com/haskell/haskell-language-server/pull/4867"><span>#4867</span></a> improving
HLS’s response for <code>prepareRename</code>,</li>
<li><a href="https://github.com/haskell/haskell-language-server/pull/4865"><span>#4865</span></a> migrating
the Stackage build to use GitHub actions,</li>
<li><a href="https://github.com/haskell/haskell-language-server/pull/4854"><span>#4854</span></a> fixing
a redundant hash in the ghcide cache path.</li>
</ul></li>
<li><p><a href="https://well-typed.com/people/hannes">Hannes</a> released version <code>0.19.0</code> of <code>hie-bios</code> (<a href="https://github.com/haskell/hie-bios/pull/506"><span>#506</span></a>).</p></li>
<li><p><a href="https://well-typed.com/people/sam">Sam</a> fixed a <code>hie-bios</code> regression in which the local build directory would get overwritten,
leading to cache trashing (<a href="https://github.com/haskell/hie-bios/issues/501"><span>#501</span></a>, <a href="https://github.com/haskell/hie-bios/pull/503"><span>#503</span></a>).</p></li>
</ul>
<h3 id="haskell-syntax-highlighting">Haskell syntax highlighting</h3>
<p><a href="https://well-typed.com/people/sam">Sam</a> fixed a <a href="https://github.com/JustusAdam/language-haskell/blob/master/CHANGELOG.md#370---09062026">large number of bugs</a>
in the <code>language-haskell</code> VS Code syntax highlighting extension.</p>
<h2 id="haskell-debugger">Haskell Debugger</h2>
<p><a href="https://well-typed.com/people/rodrigo">Rodrigo</a> continued to lead development of the
<a href="https://github.com/well-typed/haskell-debugger">Haskell Debugger</a> (<code>hdb</code>), an
interactive step-through debugger for Haskell,
with contributions from <a href="https://well-typed.com/people/andrea">Andrea</a> and <a href="https://well-typed.com/people/hannes">Hannes</a>.
Built on top of the bytecode
interpreter, it implements the <a href="https://microsoft.github.io/debug-adapter-protocol/">Debug Adapter Protocol</a>,
allowing debugging of Haskell programs directly in the editor. We:</p>
<ul>
<li><p>added a new RTS message to GHC allowing a thread’s flags (TSO flags)
to be set or unset (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15831">!15831</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/27131">#27131</a>), which is needed to safely implement
features like pausing, or toggling step-out/step-in per thread;</p></li>
<li><p>arranged for <code>hdb</code> to run the debuggee directly in the terminal
when using the external interpreter
(<a href="https://github.com/well-typed/haskell-debugger/pull/260"><span>#260</span></a>);</p></li>
<li><p>added support for custom external interpreter commands to GHC (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15676">!15676</a>),
making use of them rather than parsing heap terms (<a href="https://github.com/well-typed/haskell-debugger/pull/222"><span>#222</span></a>);</p></li>
<li><p>fixed a bug where forcing a thunk would invalidate stack frames on
all threads rather than just the right one
(<a href="https://github.com/well-typed/haskell-debugger/pull/292"><span>#292</span></a>), and fixed
breakpoints being overwritten by looking for active breaks in the correct
<code>ModBreaks</code>
(<a href="https://github.com/well-typed/haskell-debugger/pull/298"><span>#298</span></a>);</p></li>
<li><p>refactored the home-unit session initialisation
(<a href="https://github.com/well-typed/haskell-debugger/pull/279"><span>#279</span></a>).</p></li>
</ul>
<p><a href="https://well-typed.com/people/andrea">Andrea</a> made various quality-of-life improvements to the debugger:</p>
<ul>
<li>allowing imports at the REPL
(<a href="https://github.com/well-typed/haskell-debugger/pull/229"><span>#229</span></a>);</li>
<li>bringing
interactive imports back into scope after resuming execution
(<a href="https://github.com/well-typed/haskell-debugger/pull/275"><span>#275</span></a>);</li>
<li>inheriting the entry file’s language extensions
(<a href="https://github.com/well-typed/haskell-debugger/pull/230"><span>#230</span></a>);</li>
<li>adding
<code>LogMessage</code> support to implement log points
(<a href="https://github.com/well-typed/haskell-debugger/pull/223"><span>#223</span></a>);</li>
<li>sharing a single HDB+GHC session for use with HLS
(<a href="https://github.com/well-typed/haskell-debugger/pull/308"><span>#308</span></a>).</li>
</ul>
<h2 id="runtime-observability">Runtime observability</h2>
<h3 id="ghc-debug"><code>ghc-debug</code></h3>
<p><a href="https://well-typed.com/people/hannes">Hannes</a> performed various maintenance tasks on <code>ghc-debug</code>, a key tool for
out-of-process heap analysis of Haskell programs:</p>
<ul>
<li>released version 0.8.0.0 (<a href="https://gitlab.haskell.org/ghc/ghc-debug/-/merge_requests/88"><span>!88</span></a>),</li>
<li>bumped version bounds (<a href="https://gitlab.haskell.org/ghc/ghc-debug/-/merge_requests/91"><span>!91</span></a>),</li>
<li>fixed FreeBSD support (<a href="https://gitlab.haskell.org/ghc/ghc-debug/-/merge_requests/89"><span>!89</span></a>),</li>
<li>landed an old MR by Teo Camarasu, adding support for the non-moving GC (<a href="https://gitlab.haskell.org/ghc/ghc-debug/-/merge_requests/10"><span>!10</span></a>).</li>
</ul>
<h3 id="eventlog-live-eventlog-socket"><code>eventlog-live</code>, <code>eventlog-socket</code></h3>
<p><a href="https://well-typed.com/people/wen">Wen</a> continued to develop <a href="https://github.com/well-typed/eventlog-live/"><code>eventlog-live</code></a>,
which allows GHC’s eventlog to be monitored live as a program runs.
Highlights of the changes to <code>eventlog-live</code>:</p>
<ul>
<li><p>support for receiving eventlog input over TCP (<a href="https://github.com/well-typed/eventlog-live/pull/154"><span>#154</span></a>, <a href="https://github.com/well-typed/eventlog-live/pull/155"><span>#155</span></a>),</p></li>
<li><p>improved profiling support with cost-centre profiles
(<a href="https://github.com/well-typed/eventlog-live/pull/134"><span>#134</span></a>) and
<a href="https://github.com/well-typed/ghc-stack-profiler"><code>ghc-stack-profiler</code></a>
profiles (<a href="https://github.com/well-typed/eventlog-live/pull/131"><span>#131</span></a>),</p></li>
<li><p>integration with the RTS control server (<a href="https://github.com/well-typed/eventlog-live/pull/139"><span>#139</span></a>, <a href="https://github.com/well-typed/eventlog-live/pull/122"><span>#122</span></a>,
<a href="https://github.com/well-typed/eventlog-live/pull/126"><span>#126</span></a>),</p></li>
<li><p>a new demo showing <code>eventlog-live</code> monitoring GHC itself
(<a href="https://github.com/well-typed/eventlog-live/pull/138"><span>#138</span></a>,
<a href="https://github.com/well-typed/eventlog-live/pull/148"><span>#148</span></a>),</p></li>
<li><p>support for GHC 9.12 (<a href="https://github.com/well-typed/eventlog-live/pull/136"><span>#136</span></a>).</p></li>
</ul>
<p>This also included a litany of improvements to the underlying
<a href="https://github.com/well-typed/eventlog-socket/"><code>eventlog-socket</code></a> library:</p>
<ul>
<li>lifecycle hooks (<a href="https://github.com/well-typed/eventlog-socket/pull/63"><span>#63</span></a>),</li>
<li>command registration (<a href="https://github.com/well-typed/eventlog-socket/pull/45"><span>#45</span></a>),</li>
<li>support for <code>startProfTimer</code> and <code>stopProfTimer</code> (<a href="https://github.com/well-typed/eventlog-socket/pull/65"><span>#65</span></a>),</li>
<li>allow building with <code>hsc2hs</code> in cross-compilation mode (<a href="https://github.com/well-typed/eventlog-socket/pull/64"><span>#64</span></a>).</li>
</ul>
<p>Finally, <a href="https://well-typed.com/people/wen">Wen</a> also landed several supporting changes in GHC itself:</p>
<ul>
<li>fixed a race condition in the RTS between <code>flushEventLog</code> and
starting/stopping event logging (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15750">!15750</a>),</li>
<li>added a new <code>-lI</code> eventlog flag to emit IPE events (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/16004">!16004</a>),</li>
<li>added support for dynamically changing trace flags (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15936">!15936</a>).</li>
</ul>
<h3 id="ghc-stack-profiler"><code>ghc-stack-profiler</code></h3>
<p><a href="https://well-typed.com/people/hannes">Hannes</a> continued maintaining
<a href="https://github.com/well-typed/ghc-stack-profiler"><code>ghc-stack-profiler</code></a>, a
profiler that relies on stack annotations rather than heavier profiling
mechanisms:</p>
<ul>
<li><p>released version <code>0.2.0.0</code> (<a href="https://github.com/well-typed/ghc-stack-profiler/pull/24"><span>#24</span></a>);</p></li>
<li><p>made the profiler more robust, ensuring it never crashes on decoding
errors (<a href="https://github.com/well-typed/ghc-stack-profiler/pull/12"><span>#12</span></a>) and
backporting a fix for stack-decoding segmentation faults from GHC HEAD
(<a href="https://github.com/well-typed/ghc-stack-profiler/pull/20"><span>#20</span></a>);</p></li>
<li><p>integrated the profiler with
<a href="https://github.com/well-typed/eventlog-socket"><code>eventlog-socket</code></a>, adding
support for its lifecycle hooks
(<a href="https://github.com/well-typed/ghc-stack-profiler/pull/19"><span>#19</span></a>) and an
<code>eventlog-socket</code> test suite
(<a href="https://github.com/well-typed/ghc-stack-profiler/pull/25"><span>#25</span></a>);</p></li>
<li><p>improved thread management by using <code>async</code>
(<a href="https://github.com/well-typed/ghc-stack-profiler/pull/10"><span>#10</span></a>) and
exposing <code>stopStackProfilerThread</code>
(<a href="https://github.com/well-typed/ghc-stack-profiler/pull/11"><span>#11</span></a>).</p></li>
</ul>
<h2 id="ihaskell"><code>IHaskell</code></h2>
<p><a href="https://well-typed.com/people/sam">Sam</a> added Windows support to <code>IHaskell</code>, a library that provides Haskell
integration for Jupyter notebooks (<a href="https://github.com/IHaskell/IHaskell/pull/1595"><span>#1595</span></a>).</p>
<h2 id="stm-library"><code>stm</code> library</h2>
<p><a href="https://well-typed.com/people/magnus">Magnus</a> performed some important maintenance tasks on the <code>stm</code> library,
adding missing compiler versions to CI (<a href="https://github.com/haskell/stm/pull/98"><span>#98</span></a>)
and fixing a slow test (<a href="https://github.com/haskell/stm/pull/97"><span>#97</span></a>).</p>
<!-- References -->
]]></content:encoded>
</item>
<item>
    <title>Faster Cabal Haskell builds by eliminating redundant work</title>
    <link>https://well-typed.com/blog/2026/05/faster-haskell-builds</link>
    <description><![CDATA[TL;DR Build your Haskell projects 10-15% faster with this one simple trick!
(Spoiler: the simple trick is to wait for the next major cabal-install release.)

In previous work (paid for by the Sovereign Tech Fund) we
did a lot of heavy lifting to make a major architectural change to Cabal. That
work is [...]]]></description>
    <dc:creator>sam</dc:creator>
    <category>build-systems</category>
    <category>cabal</category>
    <category>community</category>
    <category>compile-time-performance</category>
    <category>open-source</category>
    <category>sovereign-tech-fund</category>
    <guid isPermaLink="false">http://www.well-typed.com/blog/2026/05/faster-haskell-builds</guid>
    <pubDate>Thu, 28 May 2026 00:00:00 GMT</pubDate>
    <content:encoded><![CDATA[<p><strong>TL;DR</strong> Build your Haskell projects 10-15% faster with this one simple trick!
(Spoiler: the simple trick is to wait for the next major <code>cabal-install</code> release.)</p>
<p>In <a href="https://well-typed.com/blog/2023/10/sovereign-tech-fund-invests-in-cabal/">previous work</a> (paid for by the Sovereign Tech Fund) we
did a lot of heavy lifting to make a major architectural change to <code>Cabal</code>. That
work is now paying off with practical benefits. This post covers follow-on
architectural improvements to <code>cabal-install</code> which then enable us to eliminate
redundant work in the <code>configure</code> phase, yielding significant reductions in
build times.</p>
<p>The changes will be available to everyone in the next major <code>cabal-install</code>
release. For a large project like <code>pandoc</code> (including all of its dependencies)
we measure a 10% (std.dev. 0.6pp) reduction in wall clock time for a 16-way
parallel build with <code>--semaphore</code>. No user changes are needed to take advantage
of this improvement.</p>
<!-- more -->
<h3 id="history-cabal-and-cabal-install">History: <code>Cabal</code> and <code>cabal-install</code></h3>
<h4 id="the-genesis-the-cabal-specification">The genesis: the <code>Cabal</code> specification</h4>
<p>First, there was <code>Cabal</code>. Its design was laid out in <a href="https://www.haskell.org/cabal/proposal/pkg-spec.pdf">A Common Architecture for Building Applications
and Tools</a>. Fundamentally, it defines the notion of a package, with
each package being built and installed with the following sequence of commands:</p>
<pre><code>&gt; hc Setup.hs
&gt; ./Setup configure
&gt; ./Setup build
&gt; ./Setup install</code></pre>
<p>Each package must be built in dependency order, with <code>hc-pkg</code> registering each
installed library into a package database.</p>
<h4 id="orchestrating-the-build-of-multiple-packages">Orchestrating the build of multiple packages</h4>
<p><code>cabal-install</code> was then born to plan and execute a build plan consisting of
many packages. With its solver, it determines a build plan, which is then
orchestrated by running the above sequence of commands for each package,
in dependency order.</p>
<p>There is however one architectural mismatch: for the solver to be able to
compute a build plan, it already needs a lot of information about the current
system:</p>
<ul>
<li>What Haskell compiler are we using?</li>
<li>What system libraries are available (<code>pkgconfig-depends</code>)?</li>
<li>What build tools are available (<code>build-tool-depends</code>)?</li>
</ul>
<p>This means that <code>cabal-install</code> already has in its hands most of the information
necessary for configuring a package; in particular it has already resolved all
the conditionals in every package description. We should thus be able to skip
most of the steps in the package’s <code>./Setup configure</code> phase. However,
the command-line interface of <code>./Setup configure</code> makes it practically
impossible to do so: passing a fully resolved dependency graph would require many
additions to the <a href="https://hackage-content.haskell.org/package/Cabal/docs/Distribution-Simple-Setup.html#t:ConfigFlags">already bloated <code>ConfigFlags</code> datatype</a>,
and a lot more data being serialised/deserialised.</p>
<p>Because of this limitation, <code>cabal-install</code>’s approach was to take its hard-won
build plan and convert it into <code>ConfigFlags</code> that specify exact dependency
versions and flag assignments. This amounts to passing <code>./Setup configure</code>
an already fully constrained configuration; the <code>configure</code> step would then
re-probe the system, re-read package databases… only to re-discover exactly
what <code>cabal-install</code> already knew!</p>
<h3 id="a-new-architecture-for-cabal-install">A new architecture for <code>cabal-install</code></h3>
<p>The paradigm shift proposed in <a href="https://well-typed.com/blog/2023/10/sovereign-tech-fund-invests-in-cabal/">our Sovereign Tech Fund proposal</a>
is that <code>cabal-install</code> should be responsible for orchestrating the whole build
process instead of running the conceptually independent build systems provided by
each package. With <code>cabal-install</code> now in control, it can directly call <code>Cabal</code>
library functions, which in turn allows skipping steps in the <code>configure</code> phase
that waste time re-discovering information that <code>cabal-install</code> is already
aware of.</p>
<p>To implement such a change, we first needed to prepare the terrain: when invoking
an external executable such as the <code>Setup</code> executable – say via the
<a href="https://hackage.haskell.org/package/process"><code>process</code> library</a> as <code>Cabal</code> uses – we can set the working
directory, environment variables and redirect input/output handles.
It was not possible to do this directly via the <code>Cabal</code> library, so we first
needed to add <code>Cabal</code> library support for <a href="https://github.com/haskell/cabal/pull/9718">setting the working directory</a>
and for <a href="https://github.com/haskell/cabal/pull/11077">choosing logging handles</a>. Once this was done,
it allowed us to refactor <code>cabal-install</code> to <a href="https://github.com/haskell/cabal/pull/11703">directly call <code>Cabal</code> library functions to build packages</a>.</p>
<h3 id="performance-impact">Performance impact</h3>
<p>This architectural change provides a solid foundation for further improvements.
The two main time sinks in the <code>Cabal</code> <code>configure</code> phase were determined to be
(using a new <a href="https://github.com/haskell/cabal/pull/11769"><code>--build-timings</code> flag to <code>cabal-install</code></a>):</p>
<ol type="1">
<li>(~50% of <code>configure</code> time) Re-configuring the compiler program database.
The compiler and <code>hc-pkg</code> were already pre-configured, but other programs
such as <code>haddock</code>, <code>ar</code>, <code>ld</code> etc were re-configured anew for each package.</li>
<li>(~40% of <code>configure</code> time) Re-probing the installed package database, via <code>hc-pkg dump</code>.</li>
</ol>
<p>We can skip this extra work by <a href="https://github.com/haskell/cabal/pull/11768">pre-configuring the compiler <code>ProgramDb</code></a>
and <a href="https://github.com/haskell/cabal/pull/11767">keeping a running <code>InstalledPackageIndex</code></a>. These
two changes, taken together, reduce the time spent in the <code>configure</code> phase by
over 90%.</p>
<p>While most of the time in builds is unsurprisingly spent… actually compiling
Haskell code [citation needed], the impact on full builds is still rather
significant. For example, when compiling <code>aeson</code> with <code>-j1</code>, we saw a reduction
in total build time of ~16.6% (std.dev. 1.9pp) in our benchmarks.</p>
<p>The fact that the <code>configure</code> phase is inherently serial also means that these
improvements have a notable impact when combined with <a href="https://well-typed.com/blog/2023/08/reducing-haskell-parallel-build-times/">the <code>-jsem</code> feature</a>.
This is because the <code>-jsem</code> feature allows us to assign more capabilities to
the <code>build</code> phase. As per <a href="https://en.wikipedia.org/wiki/Amdahl%27s_law">Amdahl’s law</a>, this results in the
<code>configure</code> phase becoming more of a bottleneck. For example, when compiling
<code>pandoc</code> with <code>cabal install pandoc -j16 --semaphore</code>, we saw a reduction in
total build time of ~10% (std.dev. 0.6pp).</p>
<h3 id="further-improvements">Further improvements</h3>
<p>These improvements provide a small glimpse of what is possible after our changes
to <code>cabal-install</code>’s architecture. A more ambitious long-term goal would be for
<code>cabal-install</code> to manage a “giant build graph” on a finer granularity level
than whole Cabal components. For example, if package <code>q</code> depends only
on module <code>P1</code> from package <code>p</code>, we could imagine starting to compile <code>q</code> after
compiling <code>P1</code> but before we have finished compiling the rest of <code>p</code>. This
would unlock build-time reductions by increasing available parallelism,
and also enable more accurate progress and error reporting.</p>
<!-- References -->
]]></content:encoded>
</item>
<item>
    <title>Exception Annotations: Lay of the Land</title>
    <link>https://well-typed.com/blog/2026/05/lay-annotation-land</link>
    <description><![CDATA[Exception annotations were introduced in GHC 9.10, and can be an invaluable tool
for debugging thorny problems. The initial implementation had some important
limitations that made them less useful in practice than one might hope, but
fortunately the situation has since been much improved. In this blog [...]]]></description>
    <dc:creator>edsko</dc:creator>
    <category>coding</category>
    <category>exceptions</category>
    <guid isPermaLink="false">http://www.well-typed.com/blog/2026/05/lay-annotation-land</guid>
    <pubDate>Fri, 08 May 2026 00:00:00 GMT</pubDate>
    <content:encoded><![CDATA[<p>Exception annotations were introduced in GHC 9.10, and can be an invaluable tool
for debugging thorny problems. The initial implementation had some important
limitations that made them less useful in practice than one might hope, but
fortunately the situation has since been much improved. In this blog post we
will give a detailed overview of the status quo as of GHC 9.12/9.14, identify
some gotchas you should be aware and provide advise on how to deal with them,
and briefly look ahead to what will change in GHC 10.0. We will also dedicate a
section to discussing the problems in GHC 9.10, for those who cannot yet
upgrade.</p>
<!-- more -->
<p>Although we will recap all necessary definitions, this blog is <em>not</em> meant to be
an introduction to exception annotations; if you have never used them before,
you might first want to watch <a href="https://www.youtube.com/watch?v=SwOkh9N41Y8&amp;list=PLD8gywOEY4HaG5VSrKVnHxCptlJv2GAn7&amp;index=30">The Haskell Unfolder Episode 29: exceptions,
annotations and backtraces</a>.</p>
<h2 id="backtraces">Backtraces</h2>
<p>Before we look at the general framework for exception annotations, let’s first
briefly recap the concept of <em>backtraces</em>, which is GHC’s answer to stack
traces in other languages. The situation is more complicated in Haskell due
laziness, and there are actually four different kinds of backtraces:</p>
<ul>
<li>based on <code>HasCallStack</code> annotations</li>
<li>based on cost-centres (which will require compiling your program with
profiling enabled)</li>
<li>based on <a href="https://well-typed.com/blog/2021/01/first-look-at-hi-profiling-mode/">IPE info</a></li>
<li>based on <a href="https://well-typed.com/blog/2020/04/dwarf-1/">DWARF info</a></li>
</ul>
<p>In this blog post we will use the first two only, but for the purposes of our
main discussion here the choice actually does not matter much; see GHC
proposal
<a href="https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/0330-exception-backtraces.rst">Decorate exceptions with backtrace information</a>
for details. If you’re interested in IPE backtraces specifically, you might also
be interested in our blog post
<a href="https://well-typed.com/blog/2025/09/better-haskell-stack-traces/">Better Haskell stack traces via user annotations</a>,
which discusses some recent extensions we implemented to improve these.</p>
<h3 id="hascallstack-backtraces"><code>HasCallStack</code> backtraces</h3>
<p>Consider this simple Haskell program, where <code>main</code> calls <code>top</code> calls <code>middle</code>
calls <code>bottom</code>:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="ot">bottom ::</span> <span class="dt">HasCallStack</span> <span class="ot">=&gt;</span> <span class="dt">IO</span> ()</span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a>bottom <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>    bt <span class="ot">&lt;-</span> collectBacktraces</span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a>    <span class="fu">putStrLn</span> <span class="op">$</span> displayBacktraces bt</span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a><span class="ot">middle ::</span> <span class="dt">HasCallStack</span> <span class="ot">=&gt;</span> <span class="dt">IO</span> ()</span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a>middle <span class="ot">=</span> bottom</span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a><span class="ot">top ::</span> <span class="dt">HasCallStack</span> <span class="ot">=&gt;</span> <span class="dt">IO</span> ()</span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a>top <span class="ot">=</span> middle</span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a><span class="ot">main ::</span> <span class="dt">IO</span> ()</span>
<span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a>main <span class="ot">=</span> top</span></code></pre></div>
<p>A <code>HasCallStack</code> is essentially an additional function argument which is
automatically populated by GHC at call sites with information about where the
function was called. When we run this program, we see something like this:</p>
<pre><code>HasCallStack backtrace:
  collectBacktraces, called at exe/DemoCallStack.hs:13:11 in (..)
  bottom, called at exe/DemoCallStack.hs:18:10 in (..)
  middle, called at exe/DemoCallStack.hs:22:7 in (..)
  top, called at exe/DemoCallStack.hs:25:8 in (..)</code></pre>
<p>The only thing worth noting here is that the moment a <code>HasCallStack</code> chain is
broken, the backtrace is cut off there. For example, if <code>middle</code> does not have a
<code>HasCallStack</code> constraint, we can no longer see where <code>middle</code> was called from:</p>
<pre><code>HasCallStack backtrace:
  collectBacktraces, called at exe/DemoCallStack.hs:19:11 in (..)
  bottom, called at exe/DemoCallStack.hs:24:10 in (..)</code></pre>
<p>The fact that <code>top</code> still has a <code>HasCallStack</code> constraint does not matter: the
callstack is cut at the first missing link.</p>
<h3 id="cost-centre-backtraces">Cost centre backtraces</h3>
<p>Cost centres are how GHC implements profiling: very roughly, the cost of a
computation is attributed to its enclosing cost centre (see chapter
<a href="https://downloads.haskell.org/ghc/latest/docs/users_guide/profiling.html">Profiling</a> of the GHC manual). Like <code>HasCallStack</code>,
this relies on source code annotations:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="ot">{-# SCC bottom #-}</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="ot">bottom ::</span> <span class="dt">HasCallStack</span> <span class="ot">=&gt;</span> <span class="dt">IO</span> ()</span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a>bottom <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a>    bt <span class="ot">&lt;-</span> collectBacktraces</span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a>    <span class="fu">putStrLn</span> <span class="op">$</span> displayBacktraces bt</span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a><span class="ot">{-# SCC middle #-}</span></span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a><span class="ot">middle ::</span> <span class="dt">IO</span> ()</span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a>middle <span class="ot">=</span> bottom</span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-11"><a href="#cb4-11" aria-hidden="true" tabindex="-1"></a><span class="ot">{-# SCC top #-}</span></span>
<span id="cb4-12"><a href="#cb4-12" aria-hidden="true" tabindex="-1"></a><span class="ot">top ::</span> <span class="dt">HasCallStack</span> <span class="ot">=&gt;</span> <span class="dt">IO</span> ()</span>
<span id="cb4-13"><a href="#cb4-13" aria-hidden="true" tabindex="-1"></a>top <span class="ot">=</span> middle</span>
<span id="cb4-14"><a href="#cb4-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-15"><a href="#cb4-15" aria-hidden="true" tabindex="-1"></a><span class="ot">{-# SCC main #-}</span></span>
<span id="cb4-16"><a href="#cb4-16" aria-hidden="true" tabindex="-1"></a><span class="ot">main ::</span> <span class="dt">IO</span> ()</span>
<span id="cb4-17"><a href="#cb4-17" aria-hidden="true" tabindex="-1"></a>main <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb4-18"><a href="#cb4-18" aria-hidden="true" tabindex="-1"></a>    setBacktraceMechanismState <span class="dt">CostCentreBacktrace</span> <span class="dt">True</span></span>
<span id="cb4-19"><a href="#cb4-19" aria-hidden="true" tabindex="-1"></a>    top</span></code></pre></div>
<p>Unlike <code>HasCallStack</code>, however, GHC offers ways for inserting such annotations
automatically, which can often make cost centre based callstacks more practical
than <code>HasCallStack</code>. The most common flag to do this is <code>-fprof-auto</code> or (in
recent GHC) <code>-fprof-late</code> (see <a href="https://well-typed.com/blog/2023/03/prof-late/">Late Cost Centre
Profiling</a>). This inserts cost centres around all
top-level functions, as we did manually above.</p>
<p>Cost centre backtraces must be explicitly enabled by calling
<a href="https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Control-Exception-Backtrace.html#v:setBacktraceMechanismState%5D"><code>setBacktraceMechanismState</code></a>, and you need
to compile your code with profiling enabled; the
<a href="https://cabal.readthedocs.io/en/stable/how-to-enable-profiling.html"><code>cabal</code> option <code>--enable-profiling</code></a>
both enables profiling as well as automatic cost centre insertion. The backtrace
for this example might look something like</p>
<pre><code>Cost-centre stack backtrace:
  DemoCallStack.main (exe/DemoCallStack.hs:(32,1)-(34,7))
  DemoCallStack.top (exe/DemoCallStack.hs:28:1-12)
  DemoCallStack.middle (exe/DemoCallStack.hs:24:1-15)
  DemoCallStack.bottom (exe/DemoCallStack.hs:(18,1)-(20,35))</code></pre>
<p>Be aware however that optimizations can delete cost centres, especially in
simple examples like this (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/27225">#27225</a>).</p>
<h3 id="cost-centres-vs-exception-handling">Cost centres vs exception handling</h3>
<p>Consider the following example: as before, <code>main</code> calls <code>top</code> calls <code>middle</code>
calls <code>bottom</code>, which prints a backtrace; however <code>bottom</code> then throws an
excepton. Meanwhile, <code>main</code> installs an exception handler called <code>handlerTop</code>,
which in turn calls <code>handlerMiddle</code> calls <code>handlerBottom</code>, which prints its
own backtrace:</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="ot">bottom ::</span> <span class="dt">HasCallStack</span> <span class="ot">=&gt;</span> <span class="dt">IO</span> ()</span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a>bottom <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a>    bt <span class="ot">&lt;-</span> collectBacktraces</span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a>    <span class="fu">putStrLn</span> <span class="op">$</span> displayBacktraces bt</span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a>    throwIO <span class="op">$</span> <span class="fu">userError</span> <span class="st">&quot;Uhoh&quot;</span></span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a><span class="ot">middle ::</span> <span class="dt">HasCallStack</span> <span class="ot">=&gt;</span> <span class="dt">IO</span> ()</span>
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a>middle <span class="ot">=</span> bottom</span>
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-10"><a href="#cb6-10" aria-hidden="true" tabindex="-1"></a><span class="ot">top ::</span> <span class="dt">HasCallStack</span> <span class="ot">=&gt;</span> <span class="dt">IO</span> ()</span>
<span id="cb6-11"><a href="#cb6-11" aria-hidden="true" tabindex="-1"></a>top <span class="ot">=</span> middle</span>
<span id="cb6-12"><a href="#cb6-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-13"><a href="#cb6-13" aria-hidden="true" tabindex="-1"></a><span class="ot">handlerBottom ::</span> <span class="dt">HasCallStack</span> <span class="ot">=&gt;</span> <span class="dt">SomeException</span> <span class="ot">-&gt;</span> <span class="dt">IO</span> ()</span>
<span id="cb6-14"><a href="#cb6-14" aria-hidden="true" tabindex="-1"></a>handlerBottom _e <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb6-15"><a href="#cb6-15" aria-hidden="true" tabindex="-1"></a>    bt <span class="ot">&lt;-</span> collectBacktraces</span>
<span id="cb6-16"><a href="#cb6-16" aria-hidden="true" tabindex="-1"></a>    <span class="fu">putStrLn</span> <span class="op">$</span> displayBacktraces bt</span>
<span id="cb6-17"><a href="#cb6-17" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-18"><a href="#cb6-18" aria-hidden="true" tabindex="-1"></a><span class="ot">handlerMiddle ::</span> <span class="dt">HasCallStack</span> <span class="ot">=&gt;</span> <span class="dt">SomeException</span> <span class="ot">-&gt;</span> <span class="dt">IO</span> ()</span>
<span id="cb6-19"><a href="#cb6-19" aria-hidden="true" tabindex="-1"></a>handlerMiddle e <span class="ot">=</span> handlerBottom e</span>
<span id="cb6-20"><a href="#cb6-20" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-21"><a href="#cb6-21" aria-hidden="true" tabindex="-1"></a><span class="ot">handlerTop ::</span> <span class="dt">HasCallStack</span> <span class="ot">=&gt;</span> <span class="dt">SomeException</span> <span class="ot">-&gt;</span> <span class="dt">IO</span> ()</span>
<span id="cb6-22"><a href="#cb6-22" aria-hidden="true" tabindex="-1"></a>handlerTop e <span class="ot">=</span> handlerMiddle e</span>
<span id="cb6-23"><a href="#cb6-23" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-24"><a href="#cb6-24" aria-hidden="true" tabindex="-1"></a><span class="ot">main ::</span> <span class="dt">IO</span> ()</span>
<span id="cb6-25"><a href="#cb6-25" aria-hidden="true" tabindex="-1"></a>main <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb6-26"><a href="#cb6-26" aria-hidden="true" tabindex="-1"></a>    setBacktraceMechanismState <span class="dt">CostCentreBacktrace</span> <span class="dt">True</span></span>
<span id="cb6-27"><a href="#cb6-27" aria-hidden="true" tabindex="-1"></a>    top  <span class="ot">`catch`</span> handlerTop</span></code></pre></div>
<p>The <code>HasCallStack</code> backtrace printed by <code>bottom</code> is</p>
<pre><code>HasCallStack backtrace:
  collectBacktraces, called at exe/DemoCCS.hs:24:11 in (..)
  bottom, called at exe/DemoCCS.hs:29:10 in (..)
  middle, called at exe/DemoCCS.hs:32:7 in (..)
  top, called at exe/DemoCCS.hs:41:5 in (..)</code></pre>
<p>as before; the <code>HasCallStack</code> printed by <code>handlerBottom</code> is very similar:</p>
<pre><code>HasCallStack backtrace:
  collectBacktraces, called at exe/DemoCCS.hs:13:11 in (..)
  handlerBottom, called at exe/DemoCCS.hs:17:19 in (..)
  handlerMiddle, called at exe/DemoCCS.hs:20:16 in (..)
  handlerTop, called at exe/DemoCCS.hs:41:18 in (..)</code></pre>
<p>For the cost-centre based backtrace, the one shown in <code>bottom</code> is as before:</p>
<pre><code>Cost-centre stack backtrace:
  DemoCCS.main (exe/DemoCCS.hs:(39,1)-(41,27))
  DemoCCS.top (exe/DemoCCS.hs:32:1-12)
  DemoCCS.middle (exe/DemoCCS.hs:29:1-15)
  DemoCCS.bottom (exe/DemoCCS.hs:(23,1)-(26,30))</code></pre>
<p>but the one shown in <code>handlerBottom</code> is more surprising:</p>
<pre><code>Cost-centre stack backtrace:
  DemoCCS.main (exe/DemoCCS.hs:(39,1)-(41,27))
  DemoCCS.top (exe/DemoCCS.hs:32:1-12)
  DemoCCS.middle (exe/DemoCCS.hs:29:1-15)
  DemoCCS.bottom (exe/DemoCCS.hs:(23,1)-(26,30))
  DemoCCS.handlerTop (exe/DemoCCS.hs:20:1-30)
  DemoCCS.handlerMiddle (exe/DemoCCS.hs:17:1-33)
  DemoCCS.handlerBottom (exe/DemoCCS.hs:(12,1)-(14,35))</code></pre>
<p>Whether or not this is expected/correct behaviour is arguable, but the rule is
this: the cost centre stack is not restored <em>until we leave the scope of
<code>catch</code></em>. Put another way: the cost centre stack reflects the fact that <code>bottom</code>
“calls” <code>handlerTop</code>, however indirectly. This applies transitively: if
<code>handlerTop</code> would throw an exception, which would then be caught by some other
exception handler, then <em>its</em> backtrace would reflect that <code>top</code> “called”
<code>handlerTop</code> “called” that other exception handler. This kind of situation can
arise quite naturally, for example when using handlers that deallocate some
resources and then <em>rethrow</em> the exception.</p>
<h2 id="basic-definitions">Basic definitions</h2>
<p>Before we look at the subtleties that arise from actually catching and throwing
(or rethrowing) exceptions, we’ll first get the basic definitions out of the
way. These have not changed much between recent GHC versions and are hopefully
uncontroversial.</p>
<h3 id="exception-annotations">Exception annotations</h3>
<p>Exceptions annotations can basically be anything at all; the only requirement is
that that we can display them:</p>
<div class="sourceCode" id="cb11"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> <span class="dt">Typeable</span> a <span class="ot">=&gt;</span> <span class="dt">ExceptionAnnotation</span> a <span class="kw">where</span></span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a><span class="ot">  displayExceptionAnnotation ::</span> a <span class="ot">-&gt;</span> <span class="dt">String</span></span></code></pre></div>
<p>An important instance of this class is <a href="https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Control-Exception-Backtrace.html#t:Backtraces"><code>Backtraces</code></a>, which
wraps a set of different kinds of backtraces:</p>
<div class="sourceCode" id="cb12"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">ExceptionAnnotation</span> <span class="dt">Backtraces</span> <span class="kw">where</span></span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a>    displayExceptionAnnotation <span class="ot">=</span> Base.displayBacktraces</span></code></pre></div>
<h3 id="exception-context">Exception context</h3>
<p>An exception context is essentially just a list of exception annotations. However,
since those annotations may be of different types, we need to wrap them in an
existential:</p>
<div class="sourceCode" id="cb13"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">ExceptionContext</span> <span class="ot">=</span> <span class="dt">ExceptionContext</span> [<span class="dt">SomeExceptionAnnotation</span>]</span>
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">SomeExceptionAnnotation</span> <span class="ot">=</span> <span class="kw">forall</span> a<span class="op">.</span> <span class="dt">ExceptionAnnotation</span> a <span class="ot">=&gt;</span> <span class="dt">SomeExceptionAnnotation</span> a</span></code></pre></div>
<p>There are functions for manipulating the exception context. The most important
are <a href="https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Control-Exception-Context.html#v:emptyExceptionContext"><code>emptyExceptionContext</code></a> and
<a href="https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Control-Exception.html#v:addExceptionContext"><code>addExceptionAnnotation</code></a>, for creating an
empty context and inserting an annotation into an existing context respectively.</p>
<div class="sourceCode" id="cb14"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a><span class="ot">emptyExceptionContext  ::</span> <span class="dt">ExceptionContext</span></span>
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a><span class="ot">addExceptionAnnotation ::</span> <span class="dt">ExceptionAnnotation</span> a <span class="ot">=&gt;</span> a <span class="ot">-&gt;</span> <span class="dt">ExceptionContext</span> <span class="ot">-&gt;</span> <span class="dt">ExceptionContext</span></span></code></pre></div>
<h3 id="pivotal-change-someexception">Pivotal change: <code>SomeException</code></h3>
<p>The pivotal change in all of this is in the definition of <code>SomeException</code> which,
starting in GHC 9.10, now has an associated list of annotations:</p>
<div class="sourceCode" id="cb15"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">SomeException</span> <span class="ot">=</span> <span class="kw">forall</span> e<span class="op">.</span> (<span class="dt">Exception</span> e, <span class="dt">HasExceptionContext</span>) <span class="ot">=&gt;</span> <span class="dt">SomeException</span> e</span>
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> <span class="dt">HasExceptionContext</span> <span class="ot">=</span> (<span class="op">?</span><span class="ot">exceptionContext ::</span> <span class="dt">ExceptionContext</span>)</span></code></pre></div>
<p>The use of an <a href="https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/implicit_parameters.html#extension-ImplicitParams">implicit parameter</a> means that pattern
matching on <code>SomeException</code> remains possible in the same way as before (though
the annotations would be silently dropped).</p>
<p>There are various functions for extracting and manipulating the exception
context associated with an exception, such as
<a href="https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Control-Exception.html#v:someExceptionContext"><code>someExceptionContext</code></a> and
<a href="https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Control-Exception.html#v:addExceptionContext"><code>addExceptionContext</code></a>:</p>
<div class="sourceCode" id="cb16"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a><span class="ot">someExceptionContext ::</span> <span class="dt">SomeException</span> <span class="ot">-&gt;</span> <span class="dt">ExceptionContext</span></span>
<span id="cb16-2"><a href="#cb16-2" aria-hidden="true" tabindex="-1"></a><span class="ot">addExceptionContext  ::</span> <span class="dt">ExceptionAnnotation</span> a <span class="ot">=&gt;</span> a <span class="ot">-&gt;</span> <span class="dt">SomeException</span> <span class="ot">-&gt;</span> <span class="dt">SomeException</span></span></code></pre></div>
<p>However, probably the most important function for extending exception contexts
is <a href="https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Control-Exception.html#v:annotateIO"><code>annotateIO</code></a>, which installs an exception handler that
extends any exception that is thrown with the specified annotation:</p>
<div class="sourceCode" id="cb17"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true" tabindex="-1"></a><span class="ot">annotateIO ::</span> <span class="kw">forall</span> e a<span class="op">.</span> <span class="dt">ExceptionAnnotation</span> e <span class="ot">=&gt;</span> e <span class="ot">-&gt;</span> <span class="dt">IO</span> a <span class="ot">-&gt;</span> <span class="dt">IO</span> a</span>
<span id="cb17-2"><a href="#cb17-2" aria-hidden="true" tabindex="-1"></a>annotateIO ann (<span class="dt">IO</span> io) <span class="ot">=</span> <span class="dt">IO</span> (PrimOp.catch<span class="op">#</span> io handler)</span>
<span id="cb17-3"><a href="#cb17-3" aria-hidden="true" tabindex="-1"></a>  <span class="kw">where</span></span>
<span id="cb17-4"><a href="#cb17-4" aria-hidden="true" tabindex="-1"></a>    handler se <span class="ot">=</span> PrimOp.raiseIO<span class="op">#</span> (addExceptionContext ann se)</span></code></pre></div>
<p>It is important to emphasize that this is implemented with
<a href="https://hackage-content.haskell.org/package/ghc-prim-0.13.0/docs/GHC-PrimopWrappers.html">primops</a>, not with the regular <code>catch</code> and <code>throwIO</code>
functions, which do considerably more than merely catching and throwing, as we
shall see.</p>
<h2 id="exception-type-class"><code>Exception</code> type class</h2>
<p>The <code>Exception</code> type class is a central abstraction in Haskell’s exception
ecosystem. As part of the exception annotation work, it has received one minor
extension, and it was changed in two not-so-minor-but-rather-subtle ways. Let’s
first get the part out of the way which has <em>not</em> changed: exceptions are no
good if we cannot see them:</p>
<div class="sourceCode" id="cb18"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb18-1"><a href="#cb18-1" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> (<span class="dt">Typeable</span> e, <span class="dt">Show</span> e) <span class="ot">=&gt;</span> <span class="dt">Exception</span> e <span class="kw">where</span></span>
<span id="cb18-2"><a href="#cb18-2" aria-hidden="true" tabindex="-1"></a><span class="ot">  displayException ::</span> e <span class="ot">-&gt;</span> <span class="dt">String</span></span>
<span id="cb18-3"><a href="#cb18-3" aria-hidden="true" tabindex="-1"></a>  displayException <span class="ot">=</span> <span class="fu">show</span></span>
<span id="cb18-4"><a href="#cb18-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb18-5"><a href="#cb18-5" aria-hidden="true" tabindex="-1"></a>  <span class="co">-- (..)</span></span></code></pre></div>
<h3 id="backtracedesired"><code>backtraceDesired</code></h3>
<p>The minor extension is a new function called
<a href="https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Control-Exception.html#v:backtraceDesired"><code>backtraceDesired</code></a>, which indicates if a backtrace
should be attached to exceptions of this type; we will see how this function is
used when we discuss the <a href="#implementation">implementation of <code>throwIO</code></a>.</p>
<div class="sourceCode" id="cb19"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb19-1"><a href="#cb19-1" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> (<span class="dt">Typeable</span> e, <span class="dt">Show</span> e) <span class="ot">=&gt;</span> <span class="dt">Exception</span> e <span class="kw">where</span></span>
<span id="cb19-2"><a href="#cb19-2" aria-hidden="true" tabindex="-1"></a>  <span class="co">-- (..)</span></span>
<span id="cb19-3"><a href="#cb19-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb19-4"><a href="#cb19-4" aria-hidden="true" tabindex="-1"></a><span class="ot">  backtraceDesired ::</span> e <span class="ot">-&gt;</span> <span class="dt">Bool</span></span>
<span id="cb19-5"><a href="#cb19-5" aria-hidden="true" tabindex="-1"></a>  backtraceDesired _ <span class="ot">=</span> <span class="dt">True</span></span></code></pre></div>
<p>The argument to <code>backtraceDesired</code> is already fully constructed exception; the
question is whether a backtrace should be <em>added</em> to that exception. In most
cases the argument can simply be ignored, but it doesn’t <em>have</em> to be. For all
but a handful of specialized cases the default implementation (indicating that
yes, we want a backtrace) will be fine.</p>
<h3 id="fromexception"><code>fromException</code></h3>
<p>The not-so-minor-but-rather-subtle changes are in <code>fromException</code> and
<code>toException</code>, which remove and add the <code>SomeException</code> wrapper around
exceptions respectively. Let’s first look at <code>fromException</code>:</p>
<div class="sourceCode" id="cb20"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb20-1"><a href="#cb20-1" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> (<span class="dt">Typeable</span> e, <span class="dt">Show</span> e) <span class="ot">=&gt;</span> <span class="dt">Exception</span> e <span class="kw">where</span></span>
<span id="cb20-2"><a href="#cb20-2" aria-hidden="true" tabindex="-1"></a>  <span class="co">-- (..)</span></span>
<span id="cb20-3"><a href="#cb20-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb20-4"><a href="#cb20-4" aria-hidden="true" tabindex="-1"></a><span class="ot">  fromException ::</span> <span class="dt">SomeException</span> <span class="ot">-&gt;</span> <span class="dt">Maybe</span> e</span>
<span id="cb20-5"><a href="#cb20-5" aria-hidden="true" tabindex="-1"></a>  fromException (<span class="dt">SomeException</span> e) <span class="ot">=</span> cast e</span></code></pre></div>
<p>This may <em>look</em> no different from the implementation
<a href="https://hackage.haskell.org/package/base-4.19.2.0/docs/src/GHC.Exception.Type.html#Exception">prior to 9.10</a>,
but recall that <code>SomeException</code> now has an additional field: the exception
annotations. As mentioned above, a pattern match like this will silently discard
those annotations.</p>
<h3 id="toexception"><code>toException</code></h3>
<p>The final function in the <code>Exception</code> class is <code>toException</code>, which is intended
to add the <code>SomeException</code> wrapper.</p>
<div class="sourceCode" id="cb21"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb21-1"><a href="#cb21-1" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> (<span class="dt">Typeable</span> e, <span class="dt">Show</span> e) <span class="ot">=&gt;</span> <span class="dt">Exception</span> e <span class="kw">where</span></span>
<span id="cb21-2"><a href="#cb21-2" aria-hidden="true" tabindex="-1"></a>  <span class="co">-- (..)</span></span>
<span id="cb21-3"><a href="#cb21-3" aria-hidden="true" tabindex="-1"></a><span class="ot">  toException ::</span> e <span class="ot">-&gt;</span> <span class="dt">SomeException</span></span></code></pre></div>
<p>Prior to 9.10, the default implementation literally just added the
<code>SomeException</code> constructor:</p>
<div class="sourceCode" id="cb22"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb22-1"><a href="#cb22-1" aria-hidden="true" tabindex="-1"></a>  <span class="co">-- implementation prior to 9.10</span></span>
<span id="cb22-2"><a href="#cb22-2" aria-hidden="true" tabindex="-1"></a>  toException <span class="ot">=</span> <span class="dt">SomeException</span></span></code></pre></div>
<p>However, starting in 9.10 we also need to give an initial value for the
exception context. The default implementation, reasonably enough, chooses the
empty context:</p>
<div class="sourceCode" id="cb23"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb23-1"><a href="#cb23-1" aria-hidden="true" tabindex="-1"></a>  <span class="co">-- implementation in 9.10, 9.12, 9.14, and 10.0</span></span>
<span id="cb23-2"><a href="#cb23-2" aria-hidden="true" tabindex="-1"></a>  toException e <span class="ot">=</span> <span class="kw">let</span> <span class="op">?</span>exceptionContext <span class="ot">=</span> emptyExceptionContext <span class="kw">in</span> <span class="dt">SomeException</span> e</span></code></pre></div>
<p>Unfortunately, however, the <em>documentation</em> of <code>toException</code> has also been
modified, and now states that <code>toException</code> <a href="https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Control-Exception.html#v:toException">should produce a <code>SomeException</code>
with no attached <code>ExceptionContext</code></a>. Personally, I think
that is a mistake (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/27194">#27194</a>); we will discuss this in
the next session.</p>
<h4 id="caution-instance-for-someexception-itself">⚠️ Caution: Instance for <code>SomeException</code> itself</h4>
<p><code>SomeException</code> <em>itself</em> is also an instance of <code>Exception</code>; <code>fromException</code> is
trivial, and <code>backtraceDesired</code> and <code>displayException</code> piggy-back on the
definition of whatever exception is wrapped:</p>
<div class="sourceCode" id="cb24"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb24-1"><a href="#cb24-1" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">Exception</span> <span class="dt">SomeException</span> <span class="kw">where</span></span>
<span id="cb24-2"><a href="#cb24-2" aria-hidden="true" tabindex="-1"></a>  fromException <span class="ot">=</span> <span class="dt">Just</span></span>
<span id="cb24-3"><a href="#cb24-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb24-4"><a href="#cb24-4" aria-hidden="true" tabindex="-1"></a>  backtraceDesired (<span class="dt">SomeException</span> e) <span class="ot">=</span> backtraceDesired e</span>
<span id="cb24-5"><a href="#cb24-5" aria-hidden="true" tabindex="-1"></a>  displayException (<span class="dt">SomeException</span> e) <span class="ot">=</span> displayException e</span>
<span id="cb24-6"><a href="#cb24-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb24-7"><a href="#cb24-7" aria-hidden="true" tabindex="-1"></a>  <span class="co">-- (..)</span></span></code></pre></div>
<p>The definition of <code>toException</code> is more problematic, however. Prior to 9.10,
calling <code>toException</code> on <code>SomeException</code> was just an identity:</p>
<div class="sourceCode" id="cb25"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb25-1"><a href="#cb25-1" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">Exception</span> <span class="dt">SomeException</span> <span class="kw">where</span></span>
<span id="cb25-2"><a href="#cb25-2" aria-hidden="true" tabindex="-1"></a>  <span class="co">-- (..)</span></span>
<span id="cb25-3"><a href="#cb25-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb25-4"><a href="#cb25-4" aria-hidden="true" tabindex="-1"></a>  <span class="co">-- Prior to 9.10</span></span>
<span id="cb25-5"><a href="#cb25-5" aria-hidden="true" tabindex="-1"></a>  toException se <span class="ot">=</span> se</span></code></pre></div>
<p>Now, however, the implementation must <em>clear</em> the existing context in order to
satisfy the contract:</p>
<div class="sourceCode" id="cb26"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb26-1"><a href="#cb26-1" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">Exception</span> <span class="dt">SomeException</span> <span class="kw">where</span></span>
<span id="cb26-2"><a href="#cb26-2" aria-hidden="true" tabindex="-1"></a>  <span class="co">-- (..)</span></span>
<span id="cb26-3"><a href="#cb26-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb26-4"><a href="#cb26-4" aria-hidden="true" tabindex="-1"></a>  toException (<span class="dt">SomeException</span> e) <span class="ot">=</span> <span class="kw">let</span> <span class="op">?</span>exceptionContext <span class="ot">=</span> emptyExceptionContext <span class="kw">in</span> <span class="dt">SomeException</span> e</span></code></pre></div>
<p>I think this is simply wrong; at the very least, it is highly counter-intuitive,
and it also does not match
<a href="https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/0330-exception-backtraces.rst">the original proposal</a>;
I don’t know why this was changed. We will see some
<a href="#caution-throwing-someexception">consequences of this design choice</a>
when we discuss throwing exceptions.</p>
<h3 id="newtype-helpers">Newtype helpers</h3>
<p>There are two auxiliary types, with their own <code>Exception</code> instances, that can be
helpful when throwing or catching exceptions in specific ways. We haven’t
discussed either throwing or catching yet, but we will nonetheless discuss these
auxiliary types first as we will need them in the subsequent sessions.</p>
<h4 id="nobacktrace"><code>NoBacktrace</code></h4>
<p><code>NoBacktrace</code> can be used to override <code>backtraceDesired</code>:</p>
<div class="sourceCode" id="cb27"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb27-1"><a href="#cb27-1" aria-hidden="true" tabindex="-1"></a><span class="kw">newtype</span> <span class="dt">NoBacktrace</span> e <span class="ot">=</span> <span class="dt">NoBacktrace</span> e</span>
<span id="cb27-2"><a href="#cb27-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb27-3"><a href="#cb27-3" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">Exception</span> e <span class="ot">=&gt;</span> <span class="dt">Exception</span> (<span class="dt">NoBacktrace</span> e) <span class="kw">where</span></span>
<span id="cb27-4"><a href="#cb27-4" aria-hidden="true" tabindex="-1"></a>  fromException <span class="ot">=</span> <span class="fu">fmap</span> <span class="dt">NoBacktrace</span> <span class="op">.</span> fromException</span>
<span id="cb27-5"><a href="#cb27-5" aria-hidden="true" tabindex="-1"></a>  toException (<span class="dt">NoBacktrace</span> e) <span class="ot">=</span> toException e</span>
<span id="cb27-6"><a href="#cb27-6" aria-hidden="true" tabindex="-1"></a>  backtraceDesired _ <span class="ot">=</span> <span class="dt">False</span></span>
<span id="cb27-7"><a href="#cb27-7" aria-hidden="true" tabindex="-1"></a>  <span class="co">-- displayException left at its default implementation</span></span></code></pre></div>
<h4 id="exceptionwithcontext"><code>ExceptionWithContext</code></h4>
<p>The other, arguably more imporant, auxiliary type is <code>ExceptionWithContext</code>.
The definition itself is straight-forward: it simply pairs some value with an
exception context:</p>
<div class="sourceCode" id="cb28"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb28-1"><a href="#cb28-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">ExceptionWithContext</span> a <span class="ot">=</span> <span class="dt">ExceptionWithContext</span> <span class="dt">ExceptionContext</span> a</span></code></pre></div>
<p>The idea is that this type gives us a way to catch exceptions of specific types (rather than catching <code>SomeException</code>), and still get access to the exception context. For example:</p>
<div class="sourceCode" id="cb29"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb29-1"><a href="#cb29-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">MyException</span> <span class="ot">=</span> <span class="dt">MyException</span></span>
<span id="cb29-2"><a href="#cb29-2" aria-hidden="true" tabindex="-1"></a>  <span class="kw">deriving</span> stock (<span class="dt">Show</span>)</span>
<span id="cb29-3"><a href="#cb29-3" aria-hidden="true" tabindex="-1"></a>  <span class="kw">deriving</span> anyclass (<span class="dt">Exception</span>)</span>
<span id="cb29-4"><a href="#cb29-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb29-5"><a href="#cb29-5" aria-hidden="true" tabindex="-1"></a><span class="ot">example ::</span> <span class="dt">IO</span> ()</span>
<span id="cb29-6"><a href="#cb29-6" aria-hidden="true" tabindex="-1"></a>example <span class="ot">=</span> someAction <span class="ot">`catch`</span> \(<span class="dt">ExceptionWithContext</span> ctxt <span class="dt">MyException</span>) <span class="ot">-&gt;</span> <span class="kw">do</span></span>
<span id="cb29-7"><a href="#cb29-7" aria-hidden="true" tabindex="-1"></a>    <span class="co">-- (..)</span></span></code></pre></div>
<p>The implementation is reasonably straight-forward:</p>
<div class="sourceCode" id="cb30"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb30-1"><a href="#cb30-1" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">Exception</span> a <span class="ot">=&gt;</span> <span class="dt">Exception</span> (<span class="dt">ExceptionWithContext</span> a) <span class="kw">where</span></span>
<span id="cb30-2"><a href="#cb30-2" aria-hidden="true" tabindex="-1"></a>    toException (<span class="dt">ExceptionWithContext</span> ctxt e) <span class="ot">=</span></span>
<span id="cb30-3"><a href="#cb30-3" aria-hidden="true" tabindex="-1"></a>        <span class="kw">case</span> toException e <span class="kw">of</span></span>
<span id="cb30-4"><a href="#cb30-4" aria-hidden="true" tabindex="-1"></a>          <span class="dt">SomeException</span> c <span class="ot">-&gt;</span></span>
<span id="cb30-5"><a href="#cb30-5" aria-hidden="true" tabindex="-1"></a>            <span class="kw">let</span> <span class="op">?</span>exceptionContext <span class="ot">=</span> ctxt</span>
<span id="cb30-6"><a href="#cb30-6" aria-hidden="true" tabindex="-1"></a>            <span class="kw">in</span> <span class="dt">SomeException</span> c</span>
<span id="cb30-7"><a href="#cb30-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb30-8"><a href="#cb30-8" aria-hidden="true" tabindex="-1"></a>    fromException se <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb30-9"><a href="#cb30-9" aria-hidden="true" tabindex="-1"></a>        e <span class="ot">&lt;-</span> fromException se</span>
<span id="cb30-10"><a href="#cb30-10" aria-hidden="true" tabindex="-1"></a>        <span class="fu">return</span> (<span class="dt">ExceptionWithContext</span> (someExceptionContext se) e)</span>
<span id="cb30-11"><a href="#cb30-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb30-12"><a href="#cb30-12" aria-hidden="true" tabindex="-1"></a>    backtraceDesired (<span class="dt">ExceptionWithContext</span> _ e) <span class="ot">=</span> backtraceDesired e</span>
<span id="cb30-13"><a href="#cb30-13" aria-hidden="true" tabindex="-1"></a>    displayException <span class="ot">=</span> displayException <span class="op">.</span> toException</span></code></pre></div>
<p>That said, the devil is very much in the detail with these kinds of definitions,
and as we shall see, it was <a href="#duplicated-annotations">defined incorrectly in GHC
9.10</a>.</p>
<h2 id="throw">Throw</h2>
<p>The primary function for throwing an exception is <code>throwIO</code>, which is defined
as<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a></p>
<div class="sourceCode" id="cb31"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb31-1"><a href="#cb31-1" aria-hidden="true" tabindex="-1"></a><span class="ot">throwIO ::</span> (<span class="dt">HasCallStack</span>, <span class="dt">Exception</span> e) <span class="ot">=&gt;</span> e <span class="ot">-&gt;</span> <span class="dt">IO</span> a</span>
<span id="cb31-2"><a href="#cb31-2" aria-hidden="true" tabindex="-1"></a>throwIO e <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb31-3"><a href="#cb31-3" aria-hidden="true" tabindex="-1"></a>    se <span class="ot">&lt;-</span> toExceptionWithBacktrace e</span>
<span id="cb31-4"><a href="#cb31-4" aria-hidden="true" tabindex="-1"></a>    <span class="dt">IO</span> (PrimOp.raiseIO<span class="op">#</span> se)</span></code></pre></div>
<p>Most of the actual work happens in <code>toExceptionWithBacktrace</code>:</p>
<div class="sourceCode" id="cb32"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb32-1"><a href="#cb32-1" aria-hidden="true" tabindex="-1"></a><span class="ot">toExceptionWithBacktrace ::</span> (<span class="dt">HasCallStack</span>, <span class="dt">Exception</span> e) <span class="ot">=&gt;</span> e <span class="ot">-&gt;</span> <span class="dt">IO</span> <span class="dt">SomeException</span></span>
<span id="cb32-2"><a href="#cb32-2" aria-hidden="true" tabindex="-1"></a>toExceptionWithBacktrace e <span class="ot">=</span></span>
<span id="cb32-3"><a href="#cb32-3" aria-hidden="true" tabindex="-1"></a>    <span class="kw">if</span> backtraceDesired e <span class="kw">then</span> <span class="kw">do</span></span>
<span id="cb32-4"><a href="#cb32-4" aria-hidden="true" tabindex="-1"></a>      bt <span class="ot">&lt;-</span> Base.collectBacktraces</span>
<span id="cb32-5"><a href="#cb32-5" aria-hidden="true" tabindex="-1"></a>      <span class="fu">return</span> (addExceptionContext bt (toException e))</span>
<span id="cb32-6"><a href="#cb32-6" aria-hidden="true" tabindex="-1"></a>    <span class="kw">else</span></span>
<span id="cb32-7"><a href="#cb32-7" aria-hidden="true" tabindex="-1"></a>      <span class="fu">return</span> (toException e)</span></code></pre></div>
<p>That is, if a backtrace is desired, we collect one and add it as an annotation
to the exception that we’re about to throw.</p>
<h3 id="generalization">Generalization</h3>
<p>In GHC 9.14 <code>toExceptionWithBacktrace</code> was generalized to</p>
<div class="sourceCode" id="cb33"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb33-1"><a href="#cb33-1" aria-hidden="true" tabindex="-1"></a><span class="ot">toExceptionWithBacktrace ::</span> (<span class="dt">HasCallStack</span>, <span class="dt">Exception</span> e) <span class="ot">=&gt;</span> e <span class="ot">-&gt;</span> <span class="dt">IO</span> <span class="dt">SomeException</span></span>
<span id="cb33-2"><a href="#cb33-2" aria-hidden="true" tabindex="-1"></a>toExceptionWithBacktrace e <span class="ot">=</span></span>
<span id="cb33-3"><a href="#cb33-3" aria-hidden="true" tabindex="-1"></a>    <span class="kw">if</span> backtraceDesired e <span class="kw">then</span> <span class="kw">do</span></span>
<span id="cb33-4"><a href="#cb33-4" aria-hidden="true" tabindex="-1"></a>      <span class="dt">SomeExceptionAnnotation</span> ea <span class="ot">&lt;-</span> collectExceptionAnnotation</span>
<span id="cb33-5"><a href="#cb33-5" aria-hidden="true" tabindex="-1"></a>      <span class="fu">return</span> (addExceptionContext ea (toException e))</span>
<span id="cb33-6"><a href="#cb33-6" aria-hidden="true" tabindex="-1"></a>    <span class="kw">else</span></span>
<span id="cb33-7"><a href="#cb33-7" aria-hidden="true" tabindex="-1"></a>      <span class="fu">return</span> (toException e)</span></code></pre></div>
<p>This is an experimental API (not yet part of <code>base</code>); see <a href="https://github.com/haskell/core-libraries-committee/issues/348">CLC #348</a>
for details. The idea is that you can use
<a href="https://hackage-content.haskell.org/package/ghc-internal-9.1401.0/docs/GHC-Internal-Exception-Backtrace.html#v:setCollectExceptionAnnotation"><code>setCollectExceptionAnnotation</code></a> to
register your own function to be run to construct an annotation whenever an
exception is thrown anywhere. For example, if you’re worried that some IO faults
are happening due to your CPU overheating, you might use</p>
<div class="sourceCode" id="cb34"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb34-1"><a href="#cb34-1" aria-hidden="true" tabindex="-1"></a><span class="kw">newtype</span> <span class="dt">Temperature</span> <span class="ot">=</span> <span class="dt">Temperature</span> <span class="dt">Int</span></span>
<span id="cb34-2"><a href="#cb34-2" aria-hidden="true" tabindex="-1"></a>  <span class="kw">deriving</span> stock (<span class="dt">Show</span>)</span>
<span id="cb34-3"><a href="#cb34-3" aria-hidden="true" tabindex="-1"></a>  <span class="kw">deriving</span> anyclass (<span class="dt">ExceptionAnnotation</span>)</span>
<span id="cb34-4"><a href="#cb34-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb34-5"><a href="#cb34-5" aria-hidden="true" tabindex="-1"></a><span class="ot">getTempCPU ::</span> <span class="dt">IO</span> <span class="dt">Temperature</span></span>
<span id="cb34-6"><a href="#cb34-6" aria-hidden="true" tabindex="-1"></a>getTempCPU <span class="ot">=</span> <span class="co">-- (..)</span></span>
<span id="cb34-7"><a href="#cb34-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb34-8"><a href="#cb34-8" aria-hidden="true" tabindex="-1"></a><span class="ot">main ::</span> <span class="dt">IO</span> ()</span>
<span id="cb34-9"><a href="#cb34-9" aria-hidden="true" tabindex="-1"></a>main <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb34-10"><a href="#cb34-10" aria-hidden="true" tabindex="-1"></a>    setCollectExceptionAnnotation getTempCPU</span>
<span id="cb34-11"><a href="#cb34-11" aria-hidden="true" tabindex="-1"></a>    <span class="co">-- (..)</span></span></code></pre></div>
<p>By default, the collection callback is <code>collectBacktraces</code>, so unless you
register a different callback the behaviour is the same as in 9.10 and 9.12.</p>
<h3 id="caution-throwing-someexception">⚠️ Caution: Throwing <code>SomeException</code></h3>
<p>Because <code>throwIO</code> calls <code>toException</code>, and since <code>toException</code> for
<code>SomeException</code>
<a href="#instance-for-someexception-itself"><em>clears the exception context</em></a>,
you probably don’t want to call <code>throwIO</code> on an argument of type
<code>SomeException</code>: any exception annotations that might be embedded in that
exception will be lost.</p>
<p>The most common case for throwing <code>SomeException</code> is <em>inside</em> an exception
handler; we will cover this specific case of <em>rethrowing</em> exceptions <a href="#caution-rethrowing-the-same-exception">when we
discuss <code>onException</code></a>, but we can
reuse the same combinators also to define a general “throw precisely this
exception” function:</p>
<div class="sourceCode" id="cb35"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb35-1"><a href="#cb35-1" aria-hidden="true" tabindex="-1"></a><span class="ot">raiseIO ::</span> <span class="dt">SomeException</span> <span class="ot">-&gt;</span> <span class="dt">IO</span> ()</span>
<span id="cb35-2"><a href="#cb35-2" aria-hidden="true" tabindex="-1"></a>raiseIO (<span class="dt">SomeException</span> e) <span class="ot">=</span> rethrowIO (<span class="dt">ExceptionWithContext</span> <span class="op">?</span>exceptionContext e)</span></code></pre></div>
<h2 id="catch">Catch</h2>
<p>The most important change in GHC 9.12 from 9.10 is in the definition of <code>catch</code>, which now implements the <a href="https://github.com/haskell/core-libraries-committee/issues/202"><code>WhileHandling</code></a> proposal. The idea is that
when we throw a new exception while handling another, we annotate that new
exception <em>with</em> the old exception: the new exception arose <em>while handling</em>
the old exception:</p>
<div class="sourceCode" id="cb36"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb36-1"><a href="#cb36-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">WhileHandling</span> <span class="ot">=</span> <span class="dt">WhileHandling</span> <span class="dt">SomeException</span> <span class="kw">deriving</span> <span class="dt">Show</span></span>
<span id="cb36-2"><a href="#cb36-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb36-3"><a href="#cb36-3" aria-hidden="true" tabindex="-1"></a><span class="fu">catch</span><span class="ot"> ::</span> <span class="dt">Exception</span> e <span class="ot">=&gt;</span> <span class="dt">IO</span> a <span class="ot">-&gt;</span> (e <span class="ot">-&gt;</span> <span class="dt">IO</span> a) <span class="ot">-&gt;</span> <span class="dt">IO</span> a</span>
<span id="cb36-4"><a href="#cb36-4" aria-hidden="true" tabindex="-1"></a><span class="fu">catch</span> (<span class="dt">IO</span> io) handler <span class="ot">=</span> <span class="dt">IO</span> <span class="op">$</span> PrimOp.catch<span class="op">#</span> io handler'</span>
<span id="cb36-5"><a href="#cb36-5" aria-hidden="true" tabindex="-1"></a>  <span class="kw">where</span></span>
<span id="cb36-6"><a href="#cb36-6" aria-hidden="true" tabindex="-1"></a>    handler' se <span class="ot">=</span></span>
<span id="cb36-7"><a href="#cb36-7" aria-hidden="true" tabindex="-1"></a>      <span class="kw">case</span> fromException se <span class="kw">of</span></span>
<span id="cb36-8"><a href="#cb36-8" aria-hidden="true" tabindex="-1"></a>        <span class="dt">Just</span> e' <span class="ot">-&gt;</span> PrimOp.catch<span class="op">#</span> (unIO (handler e')) (handler'' se)</span>
<span id="cb36-9"><a href="#cb36-9" aria-hidden="true" tabindex="-1"></a>        <span class="dt">Nothing</span> <span class="ot">-&gt;</span> PrimOp.raiseIO<span class="op">#</span> se</span>
<span id="cb36-10"><a href="#cb36-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb36-11"><a href="#cb36-11" aria-hidden="true" tabindex="-1"></a>    handler'' se se' <span class="ot">=</span> PrimOp.raiseIO<span class="op">#</span> (addExceptionContext (<span class="dt">WhileHandling</span> se) se')</span></code></pre></div>
<h3 id="caution-rethrowing-the-same-exception">⚠️ Caution: Rethrowing the same exception</h3>
<p>An important combinator for dealing with exceptions is <code>onException</code>, which
runs some specified action when an exception occurs (typically some resource
cleanup) and then rethrows the exception again:</p>
<div class="sourceCode" id="cb37"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb37-1"><a href="#cb37-1" aria-hidden="true" tabindex="-1"></a><span class="ot">onException ::</span> <span class="dt">IO</span> a <span class="ot">-&gt;</span> <span class="dt">IO</span> b <span class="ot">-&gt;</span> <span class="dt">IO</span> a</span>
<span id="cb37-2"><a href="#cb37-2" aria-hidden="true" tabindex="-1"></a>onException io what <span class="ot">=</span> io <span class="ot">`catch`</span> \e <span class="ot">-&gt;</span> <span class="kw">do</span></span>
<span id="cb37-3"><a href="#cb37-3" aria-hidden="true" tabindex="-1"></a>    _ <span class="ot">&lt;-</span> what</span>
<span id="cb37-4"><a href="#cb37-4" aria-hidden="true" tabindex="-1"></a>    throwIO (<span class="ot">e ::</span> <span class="dt">SomeException</span>)</span></code></pre></div>
<p>As written, this is suboptimal: for every layer of <code>onException</code>, we re-throw
the annotation stripped from its original annotations (due to <code>throwIO</code> and
<code>toException</code> for <code>SomeException</code>), and with a new <code>WhileHandling</code> annotation
with the original exception (due to <code>catch</code>). This result in unnecessary noise:
all the information is still there, but it’s buried. When we <em>rethrow</em> the same
exception, there is no need for <code>WhileHanding</code>: we should just throw the
original exception as-is.</p>
<p>To solve this, <code>base</code> now offers new functions specifically to
catch-and-rethrow: <code>catchNoPropagate</code><a href="#fn2" class="footnote-ref" id="fnref2" role="doc-noteref"><sup>2</sup></a> is like the old <code>catch</code>, without
the handler that adds the <code>WhileHandling</code> annotation; and <code>rethrowIO</code>, which
avoids adding a backtrace (using <a href="#nobacktrace"><code>NoBacktrace</code></a>; moreover, both
of these explicitly preserve contexts (using
<a href="https://well-typed.com/blog/exceptionwithcontext"><code>ExceptionWithContext</code></a>):</p>
<div class="sourceCode" id="cb38"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb38-1"><a href="#cb38-1" aria-hidden="true" tabindex="-1"></a><span class="ot">catchNoPropagate ::</span> <span class="dt">Exception</span> e <span class="ot">=&gt;</span> <span class="dt">IO</span> a <span class="ot">-&gt;</span> (<span class="dt">ExceptionWithContext</span> e <span class="ot">-&gt;</span> <span class="dt">IO</span> a) <span class="ot">-&gt;</span> <span class="dt">IO</span> a</span>
<span id="cb38-2"><a href="#cb38-2" aria-hidden="true" tabindex="-1"></a>catchNoPropagate (<span class="dt">IO</span> io) handler <span class="ot">=</span> <span class="dt">IO</span> <span class="op">$</span> PrimOp.catch<span class="op">#</span> io handler'</span>
<span id="cb38-3"><a href="#cb38-3" aria-hidden="true" tabindex="-1"></a>  <span class="kw">where</span></span>
<span id="cb38-4"><a href="#cb38-4" aria-hidden="true" tabindex="-1"></a>    handler' se <span class="ot">=</span></span>
<span id="cb38-5"><a href="#cb38-5" aria-hidden="true" tabindex="-1"></a>      <span class="kw">case</span> fromException se <span class="kw">of</span></span>
<span id="cb38-6"><a href="#cb38-6" aria-hidden="true" tabindex="-1"></a>        <span class="dt">Just</span> e' <span class="ot">-&gt;</span> unIO (handler e')</span>
<span id="cb38-7"><a href="#cb38-7" aria-hidden="true" tabindex="-1"></a>        <span class="dt">Nothing</span> <span class="ot">-&gt;</span> PrimOp.raiseIO<span class="op">#</span> se</span>
<span id="cb38-8"><a href="#cb38-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb38-9"><a href="#cb38-9" aria-hidden="true" tabindex="-1"></a><span class="ot">rethrowIO ::</span> <span class="dt">Exception</span> e <span class="ot">=&gt;</span> <span class="dt">ExceptionWithContext</span> e <span class="ot">-&gt;</span> <span class="dt">IO</span> a</span>
<span id="cb38-10"><a href="#cb38-10" aria-hidden="true" tabindex="-1"></a>rethrowIO e <span class="ot">=</span> throwIO (<span class="dt">NoBacktrace</span> e)</span></code></pre></div>
<p>This then enables the following improved implementation of <code>onException</code>:</p>
<div class="sourceCode" id="cb39"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb39-1"><a href="#cb39-1" aria-hidden="true" tabindex="-1"></a><span class="ot">onException ::</span> <span class="dt">IO</span> a <span class="ot">-&gt;</span> <span class="dt">IO</span> b <span class="ot">-&gt;</span> <span class="dt">IO</span> a</span>
<span id="cb39-2"><a href="#cb39-2" aria-hidden="true" tabindex="-1"></a>onException io what <span class="ot">=</span> io <span class="ot">`catchNoPropagate`</span> \e <span class="ot">-&gt;</span> <span class="kw">do</span></span>
<span id="cb39-3"><a href="#cb39-3" aria-hidden="true" tabindex="-1"></a>    _ <span class="ot">&lt;-</span> what</span>
<span id="cb39-4"><a href="#cb39-4" aria-hidden="true" tabindex="-1"></a>    rethrowIO (<span class="ot">e ::</span> <span class="dt">ExceptionWithContext</span> <span class="dt">SomeException</span>)</span></code></pre></div>
<h2 id="caution-displaying-exceptions">⚠️ Caution: Displaying exceptions</h2>
<p>The final pitfall we need to discuss is <em>displaying</em> exceptions. Usually we call
<code>displayException</code> to do so, but this does <em>not</em> show annotations. The idea is
that <code>displayException</code> is meant to render an exception for <em>users</em>, not
necessarily <em>developers</em>.<a href="#fn3" class="footnote-ref" id="fnref3" role="doc-noteref"><sup>3</sup></a> Starting withGHC 9.14 there is a separate function
<a href="https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Control-Exception.html#v:displayExceptionWithInfo"><code>displayExceptionWithInfo</code></a>, but that is not
available in GHC 9.12; moreover, even in GHC 9.14 I would advise against using
it when you are debugging, as <em>it only shows the top-level annotations</em>, making
things like <a href="#catch"><code>WhileHandling</code></a> much less useful.</p>
<p>Personally, I like to use my own custom exception handler which shows the full
exception, and makes a few other improvements also: it makes the nesting
structure clearer, and reorders annotations to improve readability; you can find
an example implementation <a href="https://gist.github.com/edsko/49cc535d712048f6cac532e8a02ea374">on GitHub</a> .</p>
<h2 id="ghc-9.10">GHC 9.10</h2>
<p>If you cannot upgrade from GHC 9.10, unfortunately the exception annotation
infrastructure has some important limitations. Upgrade if you can; if not, this
section will explain what you need to be aware of.</p>
<h3 id="lost-annotations">Lost annotations</h3>
<p>As we remarked <a href="#catch">when we discussed <code>catch</code></a>, the <code>WhileHandling</code> proposal
only got implemented in GHC 9.12. In GHC 9.10 the definition of <code>catch</code> was still
unchanged from its definition before the exception annotation proposal:</p>
<div class="sourceCode" id="cb40"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb40-1"><a href="#cb40-1" aria-hidden="true" tabindex="-1"></a><span class="fu">catch</span><span class="ot"> ::</span> <span class="dt">Exception</span> e <span class="ot">=&gt;</span> <span class="dt">IO</span> a <span class="ot">-&gt;</span> (e <span class="ot">-&gt;</span> <span class="dt">IO</span> a) <span class="ot">-&gt;</span> <span class="dt">IO</span> a</span>
<span id="cb40-2"><a href="#cb40-2" aria-hidden="true" tabindex="-1"></a><span class="fu">catch</span> (<span class="dt">IO</span> io) handler <span class="ot">=</span> <span class="dt">IO</span> <span class="op">$</span> PrimOp.catch<span class="op">#</span> io handler'</span>
<span id="cb40-3"><a href="#cb40-3" aria-hidden="true" tabindex="-1"></a>  <span class="kw">where</span></span>
<span id="cb40-4"><a href="#cb40-4" aria-hidden="true" tabindex="-1"></a>    handler' se <span class="ot">=</span></span>
<span id="cb40-5"><a href="#cb40-5" aria-hidden="true" tabindex="-1"></a>      <span class="kw">case</span> fromException se <span class="kw">of</span></span>
<span id="cb40-6"><a href="#cb40-6" aria-hidden="true" tabindex="-1"></a>        <span class="dt">Just</span> e' <span class="ot">-&gt;</span> unIO (handler e')</span>
<span id="cb40-7"><a href="#cb40-7" aria-hidden="true" tabindex="-1"></a>        <span class="dt">Nothing</span> <span class="ot">-&gt;</span> PrimOp.raiseIO<span class="op">#</span> se</span></code></pre></div>
<p>However, the
<a href="#caution-instance-for-someexception-itself"><code>Exception</code> instance for <code>SomeException</code></a>
<em>was</em> already changed, so that <code>toException</code> clears the exception context.
This means that if an exception with annotations is <em>ever</em> caught and rethrown
anywhere, in a pattern such as</p>
<div class="sourceCode" id="cb41"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb41-1"><a href="#cb41-1" aria-hidden="true" tabindex="-1"></a>someAction <span class="ot">`catch`</span> \(<span class="ot">e ::</span> <span class="dt">SomeException</span>) <span class="ot">-&gt;</span> throwIO e</span></code></pre></div>
<p>those annotations will be lost. Similarly, since <code>onException</code> had not yet been
changed either, any call to <code>onException</code>, and by implification<code>bracket</code>,
anywhere in your callstack would also lose any annotations:</p>
<div class="sourceCode" id="cb42"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb42-1"><a href="#cb42-1" aria-hidden="true" tabindex="-1"></a><span class="ot">bracket ::</span> <span class="dt">IO</span> a <span class="ot">-&gt;</span> (a <span class="ot">-&gt;</span> <span class="dt">IO</span> b) <span class="ot">-&gt;</span> (a <span class="ot">-&gt;</span> <span class="dt">IO</span> c) <span class="ot">-&gt;</span> <span class="dt">IO</span> c</span>
<span id="cb42-2"><a href="#cb42-2" aria-hidden="true" tabindex="-1"></a>bracket before after thing <span class="ot">=</span></span>
<span id="cb42-3"><a href="#cb42-3" aria-hidden="true" tabindex="-1"></a>    mask <span class="op">$</span> \restore <span class="ot">-&gt;</span> <span class="kw">do</span></span>
<span id="cb42-4"><a href="#cb42-4" aria-hidden="true" tabindex="-1"></a>      a <span class="ot">&lt;-</span> before</span>
<span id="cb42-5"><a href="#cb42-5" aria-hidden="true" tabindex="-1"></a>      r <span class="ot">&lt;-</span> restore (thing a) <span class="ot">`onException`</span> after a</span>
<span id="cb42-6"><a href="#cb42-6" aria-hidden="true" tabindex="-1"></a>      _ <span class="ot">&lt;-</span> after a</span>
<span id="cb42-7"><a href="#cb42-7" aria-hidden="true" tabindex="-1"></a>      <span class="fu">return</span> r</span>
<span id="cb42-8"><a href="#cb42-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb42-9"><a href="#cb42-9" aria-hidden="true" tabindex="-1"></a><span class="ot">onException ::</span> <span class="dt">IO</span> a <span class="ot">-&gt;</span> <span class="dt">IO</span> b <span class="ot">-&gt;</span> <span class="dt">IO</span> a</span>
<span id="cb42-10"><a href="#cb42-10" aria-hidden="true" tabindex="-1"></a>onException io what <span class="ot">=</span> io <span class="ot">`catch`</span> \e <span class="ot">-&gt;</span> <span class="kw">do</span></span>
<span id="cb42-11"><a href="#cb42-11" aria-hidden="true" tabindex="-1"></a>    _ <span class="ot">&lt;-</span> what</span>
<span id="cb42-12"><a href="#cb42-12" aria-hidden="true" tabindex="-1"></a>    throwIO (<span class="ot">e ::</span> <span class="dt">SomeException</span>)</span></code></pre></div>
<p>In both cases, <code>throwIO</code> <em>will</em> insert a new backtrace, but that backtrace will
point to where the exception was <em>rethrown</em>, not to where it was thrown
originally. What’s worse, neither <code>bracket</code> nor <code>onException</code> have a
<code>HasCallStack</code> constraint, so all we see in the callstack is the call to
<code>throwIO</code> from <code>onException</code> itself.</p>
<p>Cost centre stacks do help a bit here (provided you enable profiling): at least
you’ll get to see the full backtrace to the exception handler, and with a bit of
luck even to the original call to throw, due to the semantics of <a href="#cost-centres-vs-exception-handling">semantics of
cost centres in exception handlers</a>. That
won’t always be the case though (for example, in the case of asynchronous
exceptions), and you won’t see any of the additional annotations that might have
been added to the exception.</p>
<h3 id="duplicated-annotations">Duplicated annotations</h3>
<p>The <code>Exception</code> instance for <code>ExceptionWithContext</code> in GHC 9.10 has an
incorrect definition for <code>toException</code>:</p>
<div class="sourceCode" id="cb43"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb43-1"><a href="#cb43-1" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">Exception</span> a <span class="ot">=&gt;</span> <span class="dt">Exception</span> (<span class="dt">ExceptionWithContext</span> a) <span class="kw">where</span></span>
<span id="cb43-2"><a href="#cb43-2" aria-hidden="true" tabindex="-1"></a>  <span class="co">-- (..)</span></span>
<span id="cb43-3"><a href="#cb43-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb43-4"><a href="#cb43-4" aria-hidden="true" tabindex="-1"></a>  <span class="co">-- implementation in GHC 9.10</span></span>
<span id="cb43-5"><a href="#cb43-5" aria-hidden="true" tabindex="-1"></a>  toException (<span class="dt">ExceptionWithContext</span> ctxt e) <span class="ot">=</span></span>
<span id="cb43-6"><a href="#cb43-6" aria-hidden="true" tabindex="-1"></a>      <span class="kw">let</span> <span class="op">?</span>exceptionContext <span class="ot">=</span> ctxt <span class="kw">in</span> <span class="dt">SomeException</span> e</span></code></pre></div>
<p>(We saw <a href="#exceptionwithcontext">the correct definition</a> above.)</p>
<p>This is wrong for two reasons:</p>
<ol type="1">
<li>It does not use <code>toException</code> of the underlying type (the <code>a</code> type
parameter); in <em>most</em> cases this does not matter, because <code>toException</code>
rarely does anything interesting. Even in the case of <code>SomeException</code>, where
<code>toException</code> does something “interesting” (if perhaps ill-advised), to wit
clear the exception context, that doesn’t matter here because we are
overriding that context <em>anyway</em>. However, there <em>might</em> be types where
<code>toException</code> genuinely does something important (even if I am not aware of
any such cases currently).</li>
<li>In the specific case that <code>a</code> <em>is</em> <code>SomeException</code>, this will create a
<em>nested</em> <code>SomeException</code>: <code>SomeException (SomeException someOtherException)</code>
with two copies of the context (the annotations).</li>
</ol>
<p>The second point here is more important: if we later have exception handlers
that manipulate the exception context, they will manipulate the outer context
but not the inner. Indeed, if that “manipulation” is “<em>clear</em> the context”
(see previous section), we might end up in the somewhat bizarre situation
where these two problems cancel out: if we have</p>
<div class="sourceCode" id="cb44"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb44-1"><a href="#cb44-1" aria-hidden="true" tabindex="-1"></a>someAction <span class="ot">`catch`</span> \(<span class="dt">ExceptionWithContext</span> ctxt (<span class="ot">e ::</span> <span class="dt">SomeException</span>))</span>
<span id="cb44-2"><a href="#cb44-2" aria-hidden="true" tabindex="-1"></a>  throwIO <span class="op">$</span> <span class="dt">ExceptionWithContext</span> ctxt e</span></code></pre></div>
<p>then this exception handler will duplicate the annotations, a later exception
handler might lose the outermost annotations (previous section) but not the
inner, and all of a sudden annotations that were lost mysteriously re-appear;
see GHC ticket <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/27194">#27194</a>.</p>
<p>Unfortunately, this is not a viable workaround for the lost annotation problem,
as it changes the type of the exception nested in the (outer) <code>SomeException</code>
from whatever it really should have been to (the inner) <code>SomeException</code>, which
will break any exception handlers for that specific type.</p>
<h2 id="ghc-10.0">GHC 10.0</h2>
<p>The upcoming GHC 10.0 releases makes a few improvements to the exception
annotation infrastructure. The first important improvement is that exception
handling in <em>STM</em> was lagging behind a bit; this will be rectified
(<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/25365">#25365</a>).</p>
<p>The other important fix is in <code>onException</code>. Consider again the definition
we saw <a href="#caution-rethrowing-the-same-exception">when we discussed rethrowing exceptions</a>:</p>
<div class="sourceCode" id="cb45"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb45-1"><a href="#cb45-1" aria-hidden="true" tabindex="-1"></a><span class="ot">onException ::</span> <span class="dt">IO</span> a <span class="ot">-&gt;</span> <span class="dt">IO</span> b <span class="ot">-&gt;</span> <span class="dt">IO</span> a</span>
<span id="cb45-2"><a href="#cb45-2" aria-hidden="true" tabindex="-1"></a>onException io what <span class="ot">=</span> io <span class="ot">`catchNoPropagate`</span> \e <span class="ot">-&gt;</span> <span class="kw">do</span></span>
<span id="cb45-3"><a href="#cb45-3" aria-hidden="true" tabindex="-1"></a>    _ <span class="ot">&lt;-</span> what</span>
<span id="cb45-4"><a href="#cb45-4" aria-hidden="true" tabindex="-1"></a>    rethrowIO (<span class="ot">e ::</span> <span class="dt">ExceptionWithContext</span> <span class="dt">SomeException</span>)</span></code></pre></div>
<p>We mentioned that that <code>catchNoPropagate</code> does <em>not</em> install an exception
handler that installs a <code>WhileHandling</code> annotation, because we are rethrowing
the very same exception. However, if <code>what</code> throws an exception that is no
longer the case! The definition of <code>onException</code> is therefore modified to</p>
<div class="sourceCode" id="cb46"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb46-1"><a href="#cb46-1" aria-hidden="true" tabindex="-1"></a>onException io what <span class="ot">=</span> io <span class="ot">`catchNoPropagate`</span> \e <span class="ot">-&gt;</span> <span class="kw">do</span></span>
<span id="cb46-2"><a href="#cb46-2" aria-hidden="true" tabindex="-1"></a>    _ <span class="ot">&lt;-</span> annotateIO (whileHandling e) what</span>
<span id="cb46-3"><a href="#cb46-3" aria-hidden="true" tabindex="-1"></a>    rethrowIO (<span class="ot">e ::</span> <span class="dt">ExceptionWithContext</span> <span class="dt">SomeException</span>)</span></code></pre></div>
<p>See <a href="https://github.com/haskell/core-libraries-committee/issues/397">CLC Proposal #397</a> for details. As an example, consider what happens if the release callback
of <code>bracket</code> itself throws an exception:</p>
<div class="sourceCode" id="cb47"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb47-1"><a href="#cb47-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">ReleaseFailed</span> <span class="ot">=</span> <span class="dt">ReleaseFailed</span></span>
<span id="cb47-2"><a href="#cb47-2" aria-hidden="true" tabindex="-1"></a>  <span class="kw">deriving</span> stock (<span class="dt">Show</span>)</span>
<span id="cb47-3"><a href="#cb47-3" aria-hidden="true" tabindex="-1"></a>  <span class="kw">deriving</span> anyclass (<span class="dt">Exception</span>)</span>
<span id="cb47-4"><a href="#cb47-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb47-5"><a href="#cb47-5" aria-hidden="true" tabindex="-1"></a><span class="ot">bottom ::</span> <span class="dt">HasCallStack</span> <span class="ot">=&gt;</span> <span class="dt">IO</span> ()</span>
<span id="cb47-6"><a href="#cb47-6" aria-hidden="true" tabindex="-1"></a>bottom <span class="ot">=</span> annotateIO (<span class="dt">MyAnnotation</span> <span class="dv">123456789</span>) <span class="op">$</span> throwIO <span class="dt">MyException</span></span>
<span id="cb47-7"><a href="#cb47-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb47-8"><a href="#cb47-8" aria-hidden="true" tabindex="-1"></a><span class="ot">middle ::</span> <span class="dt">HasCallStack</span> <span class="ot">=&gt;</span> <span class="dt">IO</span> ()</span>
<span id="cb47-9"><a href="#cb47-9" aria-hidden="true" tabindex="-1"></a>middle <span class="ot">=</span> bracket (<span class="fu">return</span> ()) (\() <span class="ot">-&gt;</span> throwIO <span class="dt">ReleaseFailed</span>) <span class="op">$</span> \() <span class="ot">-&gt;</span> bottom</span>
<span id="cb47-10"><a href="#cb47-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb47-11"><a href="#cb47-11" aria-hidden="true" tabindex="-1"></a><span class="ot">top ::</span> <span class="dt">HasCallStack</span> <span class="ot">=&gt;</span> <span class="dt">IO</span> ()</span>
<span id="cb47-12"><a href="#cb47-12" aria-hidden="true" tabindex="-1"></a>top <span class="ot">=</span> middle</span></code></pre></div>
<p>With the new definition <code>onException</code> (and my custom exception display function, which is still needed), we get</p>
<pre><code>demo-bracket-release-fail: Uncaught exception of type ReleaseFailed
  ReleaseFailed
  HasCallStack backtrace:
    throwIO, called at exe/DemoBracketReleaseFail.hs:42:38 in (..)
    middle, called at exe/DemoBracketReleaseFail.hs:46:7 in (..)
    top, called at exe/DemoBracketReleaseFail.hs:55:5 in (..)
  WhileHandling
    MyException
      MyException
      MyAnnotation 123456789
      HasCallStack backtrace:
        throwIO, called at exe/DemoBracketReleaseFail.hs:38:48 in (..)
        bottom, called at exe/DemoBracketReleaseFail.hs:42:70 in (..)
        middle, called at exe/DemoBracketReleaseFail.hs:46:7 in (..)
        top, called at exe/DemoBracketReleaseFail.hs:55:5 in (..)</code></pre>
<p>Very nice!</p>
<h2 id="conclusions">Conclusions</h2>
<p>Exception annotations can be invaluable when debugging difficult problems. While
the initial implementation in GHC 9.10 had some important limitations, the
situation has since been much improved. Provided you use GHC 9.12 or later,
there are two things to pay attention to in your own code (these apply to 9.12,
9.14 and 10.0):</p>
<ul>
<li>Define your own custom function to display exceptions, which shows <em>all</em>
annotations, not just the top-level ones (or use <a href="https://gist.github.com/edsko/49cc535d712048f6cac532e8a02ea374">mine</a>).</li>
<li>Be cautious with throwing <code>SomeException</code>: <code>toException</code> for <code>SomeException</code>
will clear the exception context, which is almost certainly not what you want.
For catch-and-rethrow, use the <a href="#caution-rethrowing-the-same-exception">combinators available specifically for that
purpose</a>.</li>
</ul>
<p>That said, there are still a few minor shortcomings to be aware of:</p>
<ul>
<li><p><strong>GHC 9.12 and 9.14</strong>:</p>
<ul>
<li>Exception handling <em>in STM</em> has not yet been updated: <code>throwSTM</code> does not
collect a backtrace, and <code>catchSTM</code> does not add any <code>WhileHandling</code>
annotations (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/25365">#25365</a>).</li>
<li><code>onException</code> does not add any <code>WhileHandling</code> exceptions; as a result, if
the resource deallocation callback to <code>bracket</code> <em>itself</em> throws an
exception, the original exception will be lost.</li>
</ul>
<p>Both of these will be addressed in GHC 10.0.</p></li>
<li><p><strong>exceptions-0.10.9</strong>: this is the version of <code>exceptions</code> that is bundled
with GHC 9.12, but lags behind a bit. For example, the definition
of <code>generalBracket</code> in <a href="https://hackage.haskell.org/package/exceptions-0.10.9/docs/src/Control.Monad.Catch.html#generalBracket"><code>exceptions-0.10.9</code></a>
does not use any of the abstractions for rethrowing; this is fixed in
<a href="https://hackage-content.haskell.org/package/exceptions-0.10.12/docs/src/Control.Monad.Catch.html#generalBracket"><code>exceptions-0.10.12</code></a>.
The impact is however limited: it merely means that there are some extraneous
<code>WhileHandling</code> annotations, resulting in unnecessary noise.</p></li>
<li><p>Any catch-and-rethrow patterns implemented in other packages <em>should</em> not
lose any annotations, provided that they use <code>catch</code> from <code>base</code>.</p></li>
</ul>
<!-- References -->
<section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes">
<hr />
<ol>
<li id="fn1"><p>We will ignore calls to <code>withFrozenCallStack</code>, which hide some internal
functions from the <code>HasCallStack</code> backtrace. This makes the backtrace slightly
more readable, but does not otherwise change anything. See
<a href="https://github.com/haskell/core-libraries-committee/issues/387">CLC #387</a>.<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn2"><p>Some versions of <code>base</code> distinguish
between <code>catchExceptionNoPropagate</code> and <code>catchNoPropagate</code>, which differ only in
some strictness annotations. Strictness can make a big difference, especially
when IO actions are <code>undefined</code> rather than throwing an exception. However, this
is its own can of worms, and outside the scope of this blog post. See <a href="https://github.com/haskell/core-libraries-committee/issues/383">CLC
proposal #383</a> for some discussion.<a href="#fnref2" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn3"><p>In GHC 9.10, <code>displayException</code> <em>did</em> show
annotaitons, but this got rolled back in 9.12; see <a href="https://github.com/haskell/core-libraries-committee/issues/285">CLC #285</a> for a
detailed discussion.<a href="#fnref3" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
</ol>
</section>
]]></content:encoded>
</item>

</channel>
</rss>
