<?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>Fri, 27 Mar 2026 00:00:00 GMT</pubDate>
<lastBuildDate>Fri, 27 Mar 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>Second pre-release of hs-bindgen</title>
    <link>https://well-typed.com/blog/2026/03/hs-bindgen-alpha2</link>
    <description><![CDATA[With heartfelt thanks to the many people who have already tried hs-bindgen and
given us feedback, we have steadily been working towards the first official
release (see Contributors for the full list). In case you missed
the announcement of the first alpha, hs-bindgen is a
tool for automatic construction [...]]]></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/03/hs-bindgen-alpha2</guid>
    <pubDate>Fri, 27 Mar 2026 00:00:00 GMT</pubDate>
    <content:encoded><![CDATA[<p>With heartfelt thanks to the many people who have already tried <code>hs-bindgen</code> and
given us feedback, we have steadily been working towards the first official
release (see <a href="https://github.com/well-typed/hs-bindgen/tree/release-0.1-alpha2?tab=readme-ov-file#contributors">Contributors</a> for the full list). In case you missed
the <a href="https://well-typed.com/blog/2026/02/hs-bindgen-alpha/">announcement of the first alpha</a>, <code>hs-bindgen</code> is a
tool for automatic construction of Haskell bindings for C libraries: just point
it at a C header and let it handle the rest. Because we have fixed some critical
bugs in this alpha release, but we’re not quite ready yet for the first full
official release, we have <a href="https://github.com/well-typed/hs-bindgen/releases/tag/release-0.1-alpha2">tagged a second alpha release</a>. In the
remainder of this blog post we will briefly highlight the most important
changes; please refer to the <code>CHANGELOG.md</code> of
<a href="https://github.com/well-typed/hs-bindgen/blob/release-0.1-alpha2/hs-bindgen/CHANGELOG.md">hs-bindgen</a> and of
<a href="https://github.com/well-typed/hs-bindgen/blob/release-0.1-alpha2/hs-bindgen-runtime/CHANGELOG.md">hs-bindgen-runtime</a> for the full list of changes, as well as
for migration hints where we have introduced some minor backwards incompatible
changes.</p>
<!-- more -->
<h2 id="bugfixes">Bugfixes</h2>
<p>The most important fixes for bugs in the generated code are:</p>
<ul>
<li>The implementation of <code>peek</code> and <code>poke</code> for bitfields was broken, which could
lead to segfaults.</li>
<li>Duplicate record fields are now usable also in Template Haskell mode.</li>
<li>Patterns for unsigned enums now get the right value.</li>
</ul>
<p>We have also resolved a number of panics during code generation, but those would
not have resulted in incorrect generated code (merely in no code being generated
at all).</p>
<h2 id="new-features">New features</h2>
<ul>
<li><p><em>Implicit fields</em> arise when one struct (or union) is nested in another,
without any field name or tag:</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="kw">struct</span> outer <span class="op">{</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a>  <span class="dt">int</span> x<span class="op">;</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>  <span class="kw">struct</span> <span class="op">{</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a>    <span class="dt">int</span> y<span class="op">;</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a>    <span class="dt">int</span> z<span class="op">;</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a>  <span class="op">};</span></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a><span class="op">};</span></span></code></pre></div>
<p>We now support such implicit fields; both the inner (anonymous) struct as well
as the corresponding field of the outer struct will be named after the first
field of the inner struct<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a>:</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="kw">data</span> <span class="dt">Outer</span> <span class="ot">=</span> <span class="dt">Outer</span> {</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="ot">    x ::</span> <span class="dt">CInt</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>  ,<span class="ot"> y ::</span> <span class="dt">Outer_y</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a>  }</span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">Outer_y</span> <span class="ot">=</span> <span class="dt">Outer_y</span> {</span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a><span class="ot">    y ::</span> <span class="dt">CInt</span></span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a>  ,<span class="ot"> z ::</span> <span class="dt">CInt</span></span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a>  }</span></code></pre></div>
<p>For this particular case we <em>could</em> also have chosen to flatten the structure
and add <code>y</code> and <code>z</code> directly to <code>Outer</code>, but that does not work in all cases
(for example, when we have an anonymous struct inside a union), so instead
we opt for consistency and always generate an explicit type for the
inner struct.</p></li>
<li><p>Unnamed bit-field declarations, which are used to control padding, are now
supported:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> bar <span class="op">{</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a>  <span class="dt">signed</span> <span class="dt">char</span> x <span class="op">:</span> <span class="dv">3</span><span class="op">;</span></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a>  <span class="dt">signed</span> <span class="dt">char</span>   <span class="op">:</span> <span class="dv">3</span><span class="op">;</span>  <span class="co">// Explicit padding</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a>  <span class="dt">signed</span> <span class="dt">char</span> y <span class="op">:</span> <span class="dv">2</span><span class="op">;</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a><span class="op">};</span></span></code></pre></div></li>
<li><p>We used to distinguish between <em>parse</em> predicates (which files should
<code>hs-bindgen</code> parse at all?) and <em>selection</em> predicates (for which C
declarations should we generate Haskell declarations?). This was confusing,
and as we are getting better at skipping over declarations with unsupported
features (and that list is dwinding anyway), parse predicates are not that
useful anymore. Parse predicates therefore have been removed entirely; we
simply always parse everything (<em>selection</em> predicates are still very much an
important feature of course).</p></li>
<li><p>Some infrastructure for and around binding specifications has been improved.
For example, we now distinguish between macros and non-macros of the same name,
and our treatment of arrays has changed slightly. For example, given</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="kw">typedef</span> <span class="dt">char</span> T <span class="op">[];</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> foo <span class="op">(</span>T xs<span class="op">);</span></span></code></pre></div>
<p>we now generate</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><span class="ot">foo ::</span> <span class="dt">Ptr</span> (<span class="dt">Elem</span> <span class="dt">T</span>) <span class="ot">-&gt;</span> <span class="dt">IO</span> ()</span></code></pre></div>
<p>We do not use <code>Ptr CChar</code>, because <code>T</code> might have an existing binding in
another library (with an external binding specification), and we don’t know
what the type of the elements of <code>T</code> are (it could for example be some
newtype around <code>CChar</code>). <code>Elem</code> is a member of a new <code>IsArray</code> class, part of
the <code>hs-bindgen-runtime</code>.</p></li>
<li><p>Top-level anonymous enums are now supported. For example,</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="kw">enum</span> <span class="op">{</span>A<span class="op">,</span> B<span class="op">};</span></span></code></pre></div>
<p>results in</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><span class="kw">pattern</span> <span class="dt">A</span><span class="ot"> ::</span> <span class="dt">CUInt</span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a><span class="kw">pattern</span> <span class="dt">A</span> <span class="ot">=</span> <span class="dv">0</span></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a><span class="kw">pattern</span> <span class="dt">B</span><span class="ot"> ::</span> <span class="dt">CUInt</span></span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a><span class="kw">pattern</span> <span class="dt">B</span> <span class="ot">=</span> <span class="dv">1</span></span></code></pre></div>
<p>(Normally an <code>enum</code> results in a <code>newtype</code> around the enum’s underlying type,
and the patterns are for that <code>newtype</code> instead.)</p></li>
<li><p>We now generate bindings for static global variables (such globals are
sometimes used in headers that also contain static function bodies).</p></li>
<li><p>All definitions required by the generated code are now (re-)exported from
<code>hs-bindgen-runtime</code>, so that it becomes the only package dependency that
needs to be declared (no need for <code>ghc-prim</code> or <code>primitive</code> anymore).</p></li>
</ul>
<p>This list is not complete; some other less common edge cases have also been
implemented.</p>
<h2 id="conclusions">Conclusions</h2>
<p>Although we are still working on some finishing touches before we can release
the first official version of <code>hs-bindgen</code>, it is already being put to good use
on various projects. There are only a <a href="https://github.com/well-typed/hs-bindgen/issues?q=is%3Aissue%20state%3Aopen%20label%3Amissing-c-feature">handful of missing C
features</a> left, all of which low priority edge cases (though
if you have a specific use case for any of these, do let us know!). So if you
are interested, please do try it out, and let us know if you find any problems.
There should be no major breaking changes between now and the first official
release.</p>
<section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes">
<hr />
<ol>
<li id="fn1"><p>This is the version that uses the
<code>--omit-field-prefixes</code> option, which generates code that relies on
<code>DuplicateRecordFields</code> and <code>OverloadedRecordDot</code>.<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
</ol>
</section>
]]></content:encoded>
</item>
<item>
    <title>Haskell ecosystem activities report: December 2025–February 2026</title>
    <link>https://well-typed.com/blog/2026/03/haskell-ecosystem-report-q1-2026</link>
    <description><![CDATA[This is the thirtieth 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
December 2025 to February 2026.

You can find the previous editions [...]]]></description>
    <dc:creator>adam, andreask, hannes, magnus, matthew, mikolaj, rodrigo, sam, 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/03/haskell-ecosystem-report-q1-2026</guid>
    <pubDate>Thu, 19 Mar 2026 00:00:00 GMT</pubDate>
    <content:encoded><![CDATA[<p>This is the thirtieth 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
December 2025 to February 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="team">Team</h2>
<!-- more -->
<p><a href="https://well-typed.com/people/matthew">Matthew Pickering</a> announced that he will be leaving the company and moving to a non-Haskell
role at the end of March.
Working with Matt has been a joy – more than his deep technical insight
or sharp intuition, it’s the warmth of his vision for how to work together and
his generosity that has made him such a force within the team.
He was also a beacon that could rally the community in difficult times, perhaps
most memorably with his technical and social contributions in consolidating
Haskell IDEs with the creation of the Haskell Language Server.
His dedication to tooling has also been an inspiration, with his work on
<code>ghc-debug</code> and on profiling an invaluable contribution to our understanding
of memory usage of Haskell programs.</p>
<p>The Haskell toolchain team at Well-Typed currently includes:</p>
<ul>
<li><a href="https://well-typed.com/people/andreask">Andreas Klebinger</a></li>
<li><a href="https://well-typed.com/people/hannes">Hannes Siebenhandl</a></li>
<li><a href="https://well-typed.com/people/magnus">Magnus Viernickel</a></li>
<li><a href="https://well-typed.com/people/mikolaj">Mikolaj Konarski</a></li>
<li><a href="https://well-typed.com/people/rodrigo">Rodrigo Mesquita</a></li>
<li><a href="https://well-typed.com/people/sam">Sam Derbyshire</a></li>
<li><a href="https://well-typed.com/people/zubin">Zubin Duggal</a></li>
</ul>
<p>In addition, many others within Well-Typed contribute to GHC, Cabal, HLS
and other open source Haskell libraries and tools.
This report includes contributions from <a href="https://well-typed.com/people/alex">Alex Washburn</a>,
<a href="https://well-typed.com/people/duncan">Duncan Coutts</a>,
<a href="https://well-typed.com/people/wen">Wen Kokke</a> and <a href="https://well-typed.com/people/wolfgang">Wolfgang Jeltsch</a> in
particular.</p>
<p>We are active participants in community efforts for developing the Haskell language and libraries.
Rodrigo joined the <a href="https://github.com/ghc-proposals/ghc-proposals">GHC Steering Committee</a> in December,
alongside <a href="https://well-typed.com/people/adam">Adam Gundry</a>.
Wolfgang joined the <a href="https://github.com/haskell/core-libraries-committee">Core Libraries Committee</a> in February.</p>
<h2 id="highlights">Highlights</h2>
<h3 id="interactive-step-through-debugging">Interactive step-through debugging</h3>
<p>The <a href="https://well-typed.github.io/haskell-debugger/">Haskell Debugger</a> (<code>hdb</code>) has been made more robust and more features were implemented by Rodrigo, Matthew, and Hannes.
Most notably, the debugger now:</p>
<ul>
<li>Displays stack traces for bytecode and compiled code frames (provided the program and dependencies were compiled with <code>-finfo-table-map</code> for the latter)</li>
<li>Displays source locations and callstacks for exception breakpoints</li>
<li>Uses the external interpreter by default</li>
<li>Can be run on GHC itself!</li>
</ul>
<p>To run <code>hdb</code> you need to use GHC 9.14 and to configure the IDE accordingly. Please refer to the <a href="https://well-typed.github.io/haskell-debugger/#installation">installation instructions</a>. Apart from that, if HLS just works on your codebase, so should the debugger!</p>
<h3 id="live-monitoring-using-the-eventlog">Live monitoring using the eventlog</h3>
<p>GHC’s eventlog already lets Haskell programs emit rich runtime telemetry, but
the workflow has historically been to run the program to completion and inspect
the eventlog afterwards. <a href="https://github.com/well-typed/eventlog-live/"><code>eventlog-live</code></a>
allows us instead to monitor the program as it is running. Wen continued work on
this project, taking significant steps towards making it production-ready, including:</p>
<ul>
<li><p>extending <code>eventlog-live</code> with support for the OpenTelemetry protocol
(<a href="https://github.com/well-typed/eventlog-live/pull/119"><span>#119</span></a>),</p></li>
<li><p>bringing the underlying
<a href="https://github.com/well-typed/eventlog-socket/"><code>eventlog-socket</code></a> library
closer to being ready for general use, by
adding a testsuite (<a href="https://github.com/well-typed/eventlog-socket/pull/27"><span>#27</span></a>).
fixing a litany of issues with the C code
(<a href="https://github.com/well-typed/eventlog-socket/pull/38"><span>#38</span></a>), and
finalising the user-facing API (<a href="https://github.com/well-typed/eventlog-socket/pull/43"><span>#43</span></a>),</p></li>
<li><p>adding support for custom commands in
<a href="https://github.com/well-typed/eventlog-socket/"><code>eventlog-socket</code></a>
(<a href="https://github.com/well-typed/eventlog-socket/pull/36"><span>#36</span></a>).</p></li>
</ul>
<h3 id="trees-that-grow">Trees That Grow</h3>
<p>The <code>Language.Haskell.Syntax</code> module hierarchy is intended to be a stable,
public API for the Haskell AST — one that external tools could eventually depend
on without coupling themselves to GHC internals, reducing ecosystem breakage.
Right now, that goal is undermined by lingering dependencies on internal modules
under the <code>GHC</code> hierarchy.</p>
<p>Alex, with help from Rodrigo, has been systematically removing these edges in
the dependency graph:</p>
<ul>
<li><code>Language.Haskell.Syntax.Type</code> no longer depends <code>GHC.Utils.Panic</code>
(<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15134">!15134</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26626">#26626</a>).</li>
<li><code>Language.Haskell.Syntax.Decls</code> no longer depends on <code>GHC.Unit.Module.Warnings</code>
(<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15146">!15146</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26636">#26636</a>), nor on <code>GHC.Types.ForeignCall</code> (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15477">!15477</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26700">#26700</a>) or
<code>GHC.Types.Basic</code> (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15265">!15265</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26699">#26699</a>).</li>
<li><code>Language.Haskell.Syntax.Binds</code> no longer depends on <code>GHC.Types.Basic</code>
(<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15187">!15187</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26670">#26670</a>).</li>
</ul>
<p>Once this work is done, it will be possible to consider moving the AST into a
separate package, and taking further steps towards increasing modularity of the
compiler.</p>
<h3 id="towards-a-standalone-base-package">Towards a standalone <code>base</code> package</h3>
<p>Historically, the <code>base</code> package was used as both the user-facing standard
library and a repository of GHC-specific internals, with much special treatment
in the compiler. This means GHC and <code>base</code> versions are tightly coupled, and
makes upgrading to new compiler versions unnecessarily difficult.</p>
<p>GHC developers have made significant progress towards making <code>base</code> a normal Haskell
package: <code>ghc-internal</code> has been split out as a separate library, <code>base</code> no
longer has a privileged unit-id in the compiler, and Cabal now allows
reinstalling it.</p>
<p>Matt posted a <a href="https://github.com/haskell/core-libraries-committee/issues/375#issuecomment-3646780735">summary of progress</a>
and outlined possible next steps
to seek community consensus on the direction of travel.
The <a href="https://github.com/well-typed/reinstallable-base">reinstallable-base</a> repository
collects documents and discussion on the effort.</p>
<p>Wolfgang continued various pieces of technical groundwork:</p>
<ul>
<li><p>cleaning up many unused known-key names in the compiler
(<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15184">!15184</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15190">!15190</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15211">!15211</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15213">!15213</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15217">!15217</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15218">!15218</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15219">!15219</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15215">!15215</a>),</p></li>
<li><p>finishing the process of removing <code>GHC.Desugar</code> from <code>base</code> (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15433">!15433</a>),</p></li>
<li><p>refining the import list of <code>System.IO.OS</code> to aid in modularity (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15567">!15567</a>).</p></li>
</ul>
<p>Wolfgang improved the public API of <code>base</code> relating to OS handles, to make the
API more stable across platforms and avoid the need for users to depend on
GHC-internal implementation details (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14732">!14732</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14905">!14905</a>). While in the area, he
fixed a bug in the implementation of <code>hIsReadable</code> and <code>hIsWritable</code> for duplex
handles (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26479">#26479</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15227">!15227</a>), and a mistake in the documentation of <code>hIsClosed</code>
(<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15228">!15228</a>).</p>
<h3 id="incorrect-absence-analysis-in-ghc">Incorrect absence analysis in GHC</h3>
<p>GHC bug <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26416">#26416</a> has occupied the attention of the team for quite some time.
Initially thought to be an issue with specialisation, a reproducer that Sam and
Magnus created showed that the issue is in fact a bug in absence analysis
— an optimisation that identifies and removes unused function arguments —
in which GHC would erroneously conclude that a used argument was in fact absent.</p>
<p>Andreas helped investigate the root cause, before Zubin finally took the torch
and put up a solution (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15238">!15238</a>).</p>
<h3 id="ghc-changelogs">GHC changelogs</h3>
<p>GHC’s changelogs have not always been as complete or reliable as the
community deserves. Keeping changelogs accurate across backports has also been a
major source of frustration for release managers.</p>
<p>This is why, after a discussion initiated by Teo Camarasu in <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26002">#26002</a>, we have decided
to adopt the <a href="https://codeberg.org/fgaz/changelog-d"><code>changelog.d</code></a> system —
already in use by the Cabal project — in which each change is a separate file
in the changelog directory.
This eliminates the merge conflicts that make backporting painful, and makes it
easier to associate MRs with changelog entries.</p>
<p>Zubin has been spearheading the effort, with the intention to switch to this
new method of changelog generation right after the fork date for GHC 10.0.</p>
<h2 id="ghc">GHC</h2>
<h3 id="ghc-releases">GHC Releases</h3>
<ul>
<li>Zubin worked on 9.12.3, backporting patches and preparing release candidates,
with a final release on the 27th of December.</li>
<li>Magnus and Zubin worked on backports for 9.12.4.</li>
<li>Zubin worked on 9.14.1, putting out the final release on the 19th of December.</li>
</ul>
<h3 id="frontend">Frontend</h3>
<ul>
<li><p>Sam reviewed the implementation of the <code>QualifiedStrings</code>
extension by Brandon Chinn (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14975">!14975</a>).
This allows string literals of the form <code>ModName."foo"</code>
(interpreted as <code>ModName.fromString ("foo" :: String)</code>).</p></li>
<li><p>Sam made several changes to the treatment of <code>Coercible</code> constraints in the
typechecker (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14100">!14100</a>):</p>
<ul>
<li>Defaulting of representational equalities to nominal equalities, functionality
previously added to GHC by Sam, is now more robust (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/25825">#25825</a>).</li>
<li>Error messages involving unsolved <code>Coercible</code> constraints are greatly
improved, an oft-requested improvement (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/15850">#15850</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/20289">#20289</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/23731">#23731</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26137">#26137</a>).
Error messages now consistently mention relevant out-of-scope data
constructors, provide import suggestions, and include additional
explanations about roles (when relevant).</li>
</ul></li>
<li><p>Magnus implemented several fixes to the implementation of <code>ExplicitLevelImports</code>:</p>
<ul>
<li>a missing check for types (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26098">#26098</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15119">!15119</a>),</li>
<li>a GHC panic in the driver (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26568">#26568</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15118">!15118</a>).</li>
</ul></li>
<li><p>Sam improved the reporting of “valid hole fits”, adding support for suggesting
bidirectional pattern synonyms (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26339">#26339</a>) and properly dealing with data
constructors with linear arguments (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26338">#26338</a>).</p></li>
<li><p>Sam investigated a typechecking regression starting in GHC 9.2 with the
introduction of the <code>Assert</code> type family to improve error messages involving
comparison of type-level literals (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26190">#26190</a>), posting <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26190#note_646511">his analysis</a> to the ticket.
To tackle this, he opened <a href="https://github.com/ghc-proposals/ghc-proposals/pull/735">GHC proposal <span>#735</span></a>, which is still in need of further community feedback.</p></li>
<li><p>Sam minimised a bug with rewrite rules (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26682">#26682</a>), which allowed Simon Peyton Jones
to identify and fix the bug (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15208">!15208</a>).</p></li>
<li><p>Sam improved how existential variables are displayed in Haddock documentation
(<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15099">!15099</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26252">#26252</a>).</p></li>
</ul>
<h4 id="determinism">Determinism</h4>
<ul>
<li>Matt identified and fixed several ways in which GHC compilation was not deterministic:
<ul>
<li>an issue with non-deterministic documentation information (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26858">#26858</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15482">!15482</a>).</li>
<li>non-determinism of constraint solving impacting generated <code>Typeable</code> evidence (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26846">#26846</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15442">!15442</a>).</li>
<li>issues with the Template Haskell machinery of the <code>singletons</code> library producing non-deterministic names
(<a href="https://github.com/goldfirere/singletons/pull/629"><code>singletons</code> <span>#629</span></a>,
<a href="https://github.com/goldfirere/th-desugar/pull/240"><code>th-desugar</code> <span>#240</span></a>).</li>
</ul></li>
</ul>
<h4 id="plugins">Plugins</h4>
<ul>
<li><p>Sam finished up and landed a long-standing MR by Chris Wendt (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/10133">!10133</a>) which
fixed a plugin-related issue.</p></li>
<li><p>Sam fixed a regression in <a href="https://hackage.haskell.org/package/ghc-typelits-natnormalise"><code>ghc-typelits-natnormalise</code></a>
in which the plugin would cause GHC to fall into an infinite loop
(<a href="https://github.com/clash-lang/ghc-typelits-natnormalise/issues/116"><code>ghc-typelits-natnormalise</code> <span>#116</span></a>, <a href="https://github.com/clash-lang/ghc-typelits-natnormalise/pull/118"><span>#118</span></a>).</p></li>
</ul>
<h3 id="backend">Backend</h3>
<ul>
<li><p>Rodrigo announced that work described in <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/23218">#23218</a> evolved into the <a href="https://dl.acm.org/doi/10.1145/3776711">POPL 2026
paper “Lazy Linearity for a Core Functional Language”</a>,
which presents a way to type linearity in GHC Core that is robust to almost
all GHC optimisations, together with a GHC plugin validating programs at
each optimisation stage.</p></li>
<li><p>With the oversight of Andreas, Sam carefully reconsidered the treatment of
register formats in the register allocator and liveness analysis. This
culminated in <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15121">!15121</a>:</p>
<ul>
<li>Keep track of register formats in liveness analysis (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26526">#26526</a>).</li>
<li>Use the right format when reloading spilled register (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26411">#26411</a>).</li>
<li>Enforce the invariant that writes to a register re-defined the format that
this register is used at for the purposes of liveness analysis, fixing another
bug reported by <code>@aratamizuki</code> on <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15121#note_647685"><span>!15121</span></a>.</li>
</ul></li>
<li><p>Sam put up a small fix for the mapping of registers to stack slots, fixing
an oversight in the case that registers start off small and are subsequently
written at larger widths (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26668">#26668</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15185">!15185</a>).</p></li>
<li><p>Sam reviewed a GHC contribution by <code>@sgillespie</code> adding SIMD primops for
<code>abs</code> and <code>sqrt</code> operations (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15236">!15236</a>), suggesting more efficient implementations of
certain operations.</p></li>
<li><p>Andreas investigated potential missed specialisations,
which allowed Simon Peyton Jones to make further progress in
improving the specialiser (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26831">#26831</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15441">!15441</a>).</p></li>
<li><p>Sam investigated several bugs to do with the interactions of join points with
ticks (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/14242">#14242</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26157">#26157</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26642">#26642</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26693">#26693</a>) and casts (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/14610">#14610</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/21716">#21716</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26422">#26422</a>).
He fixed the main bug (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26642">#26642</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15538">!15538</a>), which was due to incorrect
transformations in <code>mergeCaseAlts</code>. He also undertook a general refactor of
the area and, pinning down the overall handling of casts and ticks under
join points in a Note.</p></li>
</ul>
<h3 id="runtime-system-and-linker">Runtime system and linker</h3>
<ul>
<li><p>Matt fixed a decoding failure for <code>stg_dummy_ret</code> by using <code>INFO_TABLE_CONSTR</code>
for its closure (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26745">#26745</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15303">!15303</a>).</p></li>
<li><p>Duncan fixed long-standing inconsistencies in eventlog <code>STOP_THREAD</code> status
codes (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26867">#26867</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15522">!15522</a>).</p></li>
<li><p>Andreas improved the documentation of the <code>-K</code> RTS flag in <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15365">!15365</a> (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26354">#26354</a>).</p></li>
</ul>
<h3 id="exception-backtraces-stack-annotations-and-stack-decoding">Exception backtraces, stack annotations and stack decoding</h3>
<ul>
<li><p>Matt and Hannes improved the reporting of backtraces when using <code>error</code>
(<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15306">!15306</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15395">!15395</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26751">#26751</a>). This involved opening two CLC proposals
(<a href="https://github.com/haskell/core-libraries-committee/issues/383">CLC <span>#383</span></a>,
<a href="https://github.com/haskell/core-libraries-committee/issues/387">CLC <span>#387</span></a>).</p></li>
<li><p>Hannes continued working on the implementation of stack annotations and stack
decoding (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26218">#26218</a>), including:</p>
<ul>
<li>integrating <a href="https://github.com/well-typed/ghc-stack-profiler"><code>ghc-stack-profiler</code></a>,
a profiler that relies on stack annotations instead of heavier profiling
mechanisms, with the <a href="https://github.com/well-typed/eventlog-socket"><code>eventlog-socket</code></a>
library; and</li>
<li>working on the <a href="https://github.com/well-typed/ghc-stack-annotations"><code>ghc-stack-annotations</code></a>
compatibility library for annotating the stack.</li>
</ul></li>
<li><p>Rodrigo removed an incorrect assertion that fired when decoding a BCO whose
bitmap has no payload (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26640">#26640</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15136">!15136</a>).</p></li>
</ul>
<h3 id="build-system-and-packaging">Build system and packaging</h3>
<ul>
<li><p>Zubin fixed a GHC 9.14.1 build issue due to missing <code>.cabal</code> files for
<code>ghc-experimental</code> and <code>ghc-internal</code> in the source tarball (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26738">#26738</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15391">!15391</a>).</p></li>
<li><p>Andreas investigated the use of Cabal’s <code>--semaphore</code> feature to speed up GHC builds slightly (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26876">#26876</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15483">!15483</a>).
There are some issues preventing us from enabling this unconditionally
(<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26977">#26977</a>, <a href="https://github.com/haskell/cabal/issues/11557"><code>Cabal</code> <span>#11557</span></a>).</p></li>
</ul>
<h3 id="ci-and-testing">CI and testing</h3>
<ul>
<li><p>Magnus ensured the user’s guide can be generated with old versions of Python
to fix CI build failures on some older containers (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15127">!15127</a>).</p></li>
<li><p>Magnus updated the Debian images used for CI (<a href="https://gitlab.haskell.org/ghc/ci-images/-/merge_requests/183"><code>ci-images</code> <span>!183</span></a>,
<a href="https://gitlab.haskell.org/ghc/ci-images/-/merge_requests/178"><span>!178</span></a>).</p></li>
<li><p>Sam finished up the work of Sven Tennie on testing floating point expressions
in the <a href="https://gitlab.haskell.org/ghc/test-primops"><code>test-primops</code></a> test
framework for GHC (<a href="https://gitlab.haskell.org/ghc/test-primops/-/merge_requests/19"><code>test-primops</code> <span>!19</span></a>).
This is preparatory work for improving the robustness of GHC’s handling of
floating point (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26919">#26919</a>).</p></li>
<li><p>Andreas updated the <code>nofib</code> GHC benchmarking suite to fix issues that Sam ran
into when trying to use it, updating the CI in the process
(<a href="https://gitlab.haskell.org/ghc/nofib/-/merge_requests/81"><code>nofib</code> <span>!81</span></a>, <a href="https://gitlab.haskell.org/ghc/nofib/-/merge_requests/82"><span>!82</span></a>, <a href="https://gitlab.haskell.org/ghc/nofib/-/merge_requests/83"><span>!83</span></a>).</p></li>
</ul>
<h3 id="infrastructure">Infrastructure</h3>
<ul>
<li><p>Magnus worked on the infrastructure for the GitLab instance used for the GHC
project, bringing up new runners for CI and switching to a new verification
system to approve new users which makes it easier for new contributors to
open issues.</p></li>
<li><p>Magnus and Andreas helped the Haskell infrastructure team address Gitlab outages on short notice in order to improve availability of the GHC Gitlab instance.</p></li>
<li><p>Andreas and Magnus organized temporary CI capabilities sponsored by WT during a temporary outage of one of GHC’s CI runners.</p></li>
</ul>
<h2 id="cabal">Cabal</h2>
<ul>
<li><p>Sam added support for setting the logging handle via the library interface of <code>Cabal</code>,
a significant milestone in updating <code>cabal-install</code> to compile packages with
the <code>Cabal</code> library without invoking external processes (<a href="https://github.com/haskell/cabal/pull/11077"><code>Cabal</code> <span>#11077</span></a>).</p></li>
<li><p>Matt helped Matthías Páll Gissurarson to fix a bug in which <code>cabal haddock</code> was looking for
files in the wrong directory (<a href="https://github.com/haskell/cabal/issues/11475"><code>Cabal</code> <span>#11475</span></a>, <a href="https://github.com/haskell/cabal/pull/11476"><span>#11476</span></a>).</p></li>
<li><p>Matt fixed a bug with broken Haddocks locally due to non-expanded <code>${pkgroot}</code>
variable (<a href="https://github.com/haskell/cabal/issues/11217"><code>Cabal</code> <span>#11217</span></a>,
<a href="https://github.com/haskell/cabal/pull/11218"><span>#11218</span></a>).</p></li>
<li><p>Matt fixed some issues with <code>cabal repl</code> silently failing
(<a href="https://github.com/haskell/cabal/issues/11107"><code>Cabal</code> <span>#11107</span></a>,
<a href="https://github.com/haskell/cabal/pull/11237"><span>#11237</span></a>).</p></li>
</ul>
<h2 id="hls">HLS</h2>
<ul>
<li><p>In collaboration with Zubin and Andreas, Hannes investigated the root cause of
<a href="https://github.com/haskell/haskell-language-server/issues/4674">HLS issue <span>#4674</span></a>,
posting his analysis in <a href="https://github.com/haskell/haskell-language-server/issues/4674#issuecomment-3527937881">this comment</a>.
In short, the problem was that the <code>hlint</code> plugin was using an incompatible
version of <code>ghc-lib-parser</code>, and a version mismatch in this library was causing
segfaults due to changes to the <code>GHC.Data.FastString</code> implementation between
the versions.
Hannes disabled the <code>hlint</code> plugin on GHC 9.10 to work around this issue
(<a href="https://github.com/haskell/haskell-language-server/pull/4767">HLS PR <span>#4767</span></a>).</p></li>
<li><p>Hannes reviewed and assisted with <a href="https://github.com/haskell/haskell-language-server/pull/4856">HLS PR <span>#4856</span></a>
by <code>@vidit-od</code>. This PR makes HLS use the stored server-side diagnostics for
code actions, in order to make them more responsive. This fixes <a href="https://github.com/haskell/haskell-language-server/issues/4805">HLS issue <span>#4805</span></a>.</p></li>
<li><p>Hannes helped land long-running <a href="https://github.com/haskell/haskell-language-server/pull/4445">HLS PR <span>#4445</span></a>
by <code>@soulomoon</code>, which allows files to be loaded concurrently in batches in
order to improve responsiveness of HLS.</p></li>
<li><p>Zubin and Hannes worked together to update HLS to work with GHC 9.14 (<a href="https://github.com/haskell/haskell-language-server/pull/4780">HLS PR <span>#4780</span></a>).</p></li>
<li><p>Hannes worked on general maintenance of the HLS project:</p>
<ul>
<li>Prepared release 2.13.0.0 (<a href="https://github.com/haskell/haskell-language-server/pull/4785">HLS PR <span>#4785</span></a>)</li>
<li>Tackled various CI issues (<a href="https://github.com/haskell/haskell-language-server/pull/4863">HLS PR <span>#4863</span></a>,
<a href="https://github.com/haskell/haskell-language-server/pull/4812">HLS PR <span>#4812</span></a>,
<a href="https://github.com/haskell/haskell-language-server/pull/4811">HLS PR <span>#4811</span></a>)</li>
<li>Updated the advertised range of supported GHC versions (<a href="https://github.com/haskell/haskell-language-server/pull/4801">HLS PR <span>#4801</span></a>, <a href="https://github.com/haskell/haskell-language-server/pull/4799">HLS PR <span>#4799</span></a>)</li>
</ul></li>
<li><p>Hannes and Zubin implemented some fixes to Windows CI (<a href="https://github.com/haskell/haskell-language-server/pull/4800">HLS PR <span>#4800</span></a>, <a href="https://github.com/haskell/haskell-language-server/pull/4768">HLS PR <span>#4768</span></a>).</p></li>
<li><p>Hannes merged the <code>hls-module-name-plugin</code> into <code>hls-rename-plugin</code> in
<a href="https://github.com/haskell/haskell-language-server/pull/4847">HLS PR <span>#4847</span></a>.</p></li>
<li><p>Hannes improved the robustness of the <code>hls-call-hierarchy-plugin-tests</code> in
<a href="https://github.com/haskell/haskell-language-server/pull/4834">HLS PR <span>#4834</span></a>
by using <code>VirtualFileTree</code>.</p></li>
<li><p>Hannes also worked on <code>hie-bios</code>:</p>
<ul>
<li>Preparing release 0.18.0.0 (<a href="https://github.com/haskell/hie-bios/pull/496"><code>hie-bios</code> PR <span>#496</span></a>)</li>
<li>Updated the supported GHC versions (<a href="https://github.com/haskell/hie-bios/pull/495"><code>hie-bios</code> PR <span>#495</span></a>)</li>
<li>Adapted to GHC migrating some parts of its codebase to use <code>OsPath</code> (<a href="https://github.com/haskell/hie-bios/pull/493"><code>hie-bios</code> PR <span>#493</span></a>)</li>
</ul></li>
</ul>
<h2 id="haskell-debugger">Haskell Debugger</h2>
<p>Rodrigo continued work on the new <a href="https://github.com/well-typed/haskell-debugger">Haskell Debugger</a>.</p>
<ul>
<li><p>Matt and Rodrigo introduced a DSL for evaluation on the remote process, which
allows the debuggee to be queried from a custom instance, making it possible
to implement visualisations which rely on e.g. evaluatedness of a term
(<a href="https://github.com/well-typed/haskell-debugger/pull/139"><span>#139</span></a>).</p></li>
<li><p>Matt improved support for exceptions: break-on-exception breakpoints now provide
source locations (<a href="https://github.com/well-typed/haskell-debugger/pull/165"><span>#165</span></a>).</p></li>
<li><p>Rodrigo allowed call stacks to be inspected in the debugger (<a href="https://github.com/well-typed/haskell-debugger/pull/158"><span>#158</span></a>).</p></li>
<li><p>Hannes introduced support for stack decoding and viewing custom stack annotations
(<a href="https://github.com/well-typed/haskell-debugger/pull/172"><span>#172</span></a>).</p></li>
<li><p>Rodrigo made the Haskell Debugger use the external interpreter (<a href="https://github.com/well-typed/haskell-debugger/pull/170"><span>#170</span></a>),
which paves the way for multi-threaded debugging (see also <a href="https://github.com/well-typed/haskell-debugger/pull/140"><span>#140</span></a>).
This change also allowed Rodrigo to implement Windows support (<a href="https://github.com/well-typed/haskell-debugger/pull/184"><span>#184</span></a>)
with the help of Hannes.</p></li>
<li><p>Matt fixed a bug in the handling of data constructors with constraints
(<a href="https://github.com/well-typed/haskell-debugger/pull/175"><span>#175</span></a>).</p></li>
<li><p>Hannes improved caching in the CI (<a href="https://github.com/well-typed/haskell-debugger/pull/173"><span>#173</span></a>).</p></li>
</ul>
<h2 id="ghc-debug"><code>ghc-debug</code></h2>
<ul>
<li><p>Matt and Hannes fixed several issues with <code>AP_STACK</code> closures
(<a href="https://gitlab.haskell.org/ghc/ghc-debug/-/merge_requests/79"><span>!79</span></a>,
<a href="https://gitlab.haskell.org/ghc/ghc-debug/-/merge_requests/80"><span>!80</span></a>,
<a href="https://gitlab.haskell.org/ghc/ghc-debug/-/merge_requests/86"><span>!86</span></a>).</p></li>
<li><p>Hannes implemented asynchronous heap traversal in <code>ghc-debug-brick</code>,
making the interface more responsive
(<a href="https://gitlab.haskell.org/ghc/ghc-debug/-/merge_requests/78"><span>!78</span></a>).</p></li>
<li><p>Hannes added history navigation and search caching to the <code>ghc-debug-brick</code>
interface
(<a href="https://gitlab.haskell.org/ghc/ghc-debug/-/merge_requests/83"><span>!83</span></a>).</p></li>
<li><p>Hannes added a summary row to the string counting table view
(<a href="https://gitlab.haskell.org/ghc/ghc-debug/-/merge_requests/81"><span>!81</span></a>), and
fixed the search limit not being honoured during incremental searches
(<a href="https://gitlab.haskell.org/ghc/ghc-debug/-/merge_requests/76"><span>!76</span></a>).</p></li>
</ul>
]]></content:encoded>
</item>
<item>
    <title>hs-bindgen 0.1-alpha release</title>
    <link>https://well-typed.com/blog/2026/02/hs-bindgen-alpha</link>
    <description><![CDATA[Well-Typed are delighted to announce a release preview of hs-bindgen, a tool
for automatic Haskell binding generation from C header files. We hope to invite
some feedback on this initial release and then publish the first "official"
version 0.1 in a few weeks. No backwards incompatible changes are planned [...]]]></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/02/hs-bindgen-alpha</guid>
    <pubDate>Tue, 10 Feb 2026 00:00:00 GMT</pubDate>
    <content:encoded><![CDATA[<p>Well-Typed are delighted to announce a release preview of <code>hs-bindgen</code>, a tool
for automatic Haskell binding generation from C header files. We hope to invite
some feedback on this initial release and then publish the first “official”
version 0.1 in a few weeks. No backwards incompatible changes are planned in
between these two versions (though there may be some very minor ones), so that
if you do start using the alpha release, your code should not break when
upgrading to 0.1.</p>
<p>This blog post will be a brief overview of what <code>hs-bindgen</code> can do for you,
as well as a summary of the status of the project. You will find links for
further reading at the very end, the most important of which is probably the
(draft) <a href="https://github.com/well-typed/hs-bindgen/tree/main/manual">manual</a>. The
<code>hs-bindgen</code> repository also contains a number of partial
<a href="https://github.com/well-typed/hs-bindgen/tree/main/examples">example bindings</a>,
including one for <a href="https://github.com/rpm-software-management/rpm"><code>rpm</code></a>; the
<a href="https://github.com/well-typed/hs-bindgen-tutorial-nix"><code>hs-bindgen nix tutorial</code></a>
includes one further partial example set of bindings, for
<a href="https://gitlab.freedesktop.org/wlroots/wlroots"><code>wlroots</code></a>.</p>
<!-- more -->
<h2 id="installation">Installation</h2>
<p>The alpha version of <code>hs-bindgen</code> is not yet released on Hackage. Instead,
add the following to your <code>cabal.project</code> file:</p>
<pre><code>source-repository-package
  type: git
  location: https://github.com/well-typed/hs-bindgen
  tag: release-0.1-alpha
  subdir: c-expr-dsl c-expr-runtime hs-bindgen hs-bindgen-runtime

source-repository-package
  type: git
  location: https://github.com/well-typed/libclang
  tag: release-0.1-alpha</code></pre>
<p>We have however uploaded three package candidates, primarily so that they can
be used to lookup Haddocks:</p>
<ul>
<li><p><a href="https://hackage.haskell.org/package/hs-bindgen-runtime-0.1.0/candidate">hs-bindgen-runtime</a>
provides runtime support for the code generated by <code>hs-bindgen</code>, and in many
cases is also necessary for interacting with that code</p></li>
<li><p><a href="https://hackage.haskell.org/package/c-expr-runtime-0.1.0.0/candidate">c-expr-runtime</a>
provides similar support for bindings generated for CPP macros</p></li>
<li><p><a href="https://hackage.haskell.org/package/hs-bindgen-0.1.0/candidate">hs-bindgen</a>-the-library
for using <code>hs-bindgen</code> in Template Haskell mode.</p></li>
</ul>
<h2 id="introduction">Introduction</h2>
<p>We’ll start very simple. Let’s generate bindings for some library A, which offers
the following API:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> Version <span class="op">{</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>  <span class="dt">int</span> major<span class="op">;</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>  <span class="dt">int</span> minor<span class="op">;</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a><span class="op">};</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> showVersion<span class="op">(</span><span class="kw">struct</span> Version v<span class="op">);</span></span></code></pre></div>
<p>If you want to follow along, you can <a href="https://github.com/well-typed/hs-bindgen-blogpost-0.1-alpha">find the examples in this blog post
on GitHub</a>.</p>
<h3 id="invoking-hs-bindgen">Invoking <code>hs-bindgen</code></h3>
<p>There are multiple ways to integrate <code>hs-bindgen</code> into your project (we’ll
mention a few others below), but for now we will focus on just running it on the
command line:</p>
<pre><code>cabal run -- hs-bindgen-cli preprocess \
  --overwrite-files \
  --unique-id com.well-typed.hs-bindgen-0.1-alpha-blogpost \
  --enable-record-dot \
  --hs-output-dir generated \
  --module LibraryA \
  -I &quot;$(pwd)/cbits&quot; library_a.h</code></pre>
<p>The first few arguments tell <code>hs-bindgen-cli</code> that it is okay to overwrite
existing files, specify a unique identifier to avoid generating C name
collisions<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a>, enable the optional record-dot syntax to avoid
having to prefix all field names (following the recommendations in <a href="https://www.youtube.com/watch?v=9hrDm7xDpig">Haskell
Unfolder #45: Haskell records in
2025</a>), set the output directory,
and specify the desired name of the generated Haskell module.</p>
<p>The final line deserves a slightly more detailed explanation. Suppose you are
generating bindings for a library that is installed in <code>/opt/library</code>, and that
library has a header <code>/opt/library/api/server/secure.h</code>. The bindings generated
by <code>hs-bindgen</code> will need to refer to this header using a <code>#include</code> statement;
the question is what that <code>#include</code> statement should look like. If we generated</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="pp">#include </span><span class="im">&lt;/opt/library/api/server/secure.h&gt;</span></span></code></pre></div>
<p>then the generated bindings would only work on machines where that library is
installed in that exact location. If this include path should instead be</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="pp">#include </span><span class="im">&lt;api/server/secure.h&gt;</span></span></code></pre></div>
<p>with <code>/opt/library</code> in the search path, then <code>hs-bindgen</code> must be invoked with
<code>-I /opt/library</code> and main argument <code>api/server/secure.h</code>: the final argument
is included precisely as-is in the <code>#include</code>. For our simple example,
<code>-I "$(pwd)/cbits"</code> means that the <code>cbits</code> folder of our Haskell package is
added to the C include path, and the generated <code>#include</code> will be</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="pp">#include </span><span class="im">&lt;library_a.h&gt;</span></span></code></pre></div>
<h3 id="generated-bindings">Generated bindings</h3>
<p>The above invocation of <code>hs-bindgen</code> will have generated a few Haskell modules.
<code>LibraryA.hs</code> contains the translation of the <em>types</em> in the header:</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><span class="kw">module</span> <span class="dt">LibraryA</span> <span class="kw">where</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="kw">data</span> <span class="dt">Version</span> <span class="ot">=</span> <span class="dt">Version</span> {</span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a><span class="ot">    major ::</span> <span class="dt">CInt</span></span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a>  ,<span class="ot"> minor ::</span> <span class="dt">CInt</span></span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a>  }</span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a>  <span class="kw">deriving</span> stock (<span class="dt">Eq</span>, <span class="dt">Show</span>)</span>
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">Storable</span> <span class="dt">Version</span> <span class="kw">where</span> (<span class="op">..</span>)</span>
<span id="cb7-10"><a href="#cb7-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-11"><a href="#cb7-11" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">HasField</span> <span class="st">&quot;major&quot;</span> (<span class="dt">Ptr</span> <span class="dt">Version</span>) (<span class="dt">Ptr</span> <span class="dt">CInt</span>) <span class="kw">where</span> (<span class="op">..</span>)</span>
<span id="cb7-12"><a href="#cb7-12" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">HasField</span> <span class="st">&quot;minor&quot;</span> (<span class="dt">Ptr</span> <span class="dt">Version</span>) (<span class="dt">Ptr</span> <span class="dt">CInt</span>) <span class="kw">where</span> (<span class="op">..</span>)</span></code></pre></div>
<p>(The above code is slightly cleaned up for readability, and various parts of the
generated module are omitted: boilerplate such as imports and required language
extensions, Haddocks, as well as some more specialized type class instances.)</p>
<p>In addition, module <code>LibraryA/Safe.hs</code> will contain <code>safe</code> imports for all
<em>functions</em> in the header:</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">module</span> <span class="dt">LibraryA.Safe</span> <span class="kw">where</span></span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a><span class="ot">showVersion ::</span> <span class="dt">Version</span> <span class="ot">-&gt;</span> <span class="dt">IO</span> ()</span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a>showVersion <span class="ot">=</span> (<span class="op">..</span>)</span></code></pre></div>
<p>Since the C function <code>showVersion</code> takes a struct argument by value (rather than
as a pointer to the struct), which is not supported by Haskell FFI, <code>hs-bindgen</code>
will also have generated a C wrapper; that all happens transparently and
automatically.</p>
<p>Module <code>LibraryA/Unsafe.hs</code> contains the same API, but using <code>unsafe</code> imports
(if you need a refresher on <code>safe</code> versus <code>unsafe</code>, you might like to watch
<a href="https://www.youtube.com/watch?v=IMrBTx7aYjs">Haskell Unfolder #36: concurrency and the
FFI</a>). Module <code>LibraryA/FunPtr.hs</code>
finally contains the <em>addresses</em> of all functions, in case you have C code that
works with function pointers.</p>
<h3 id="using-the-generated-bindings">Using the generated bindings</h3>
<p>We can call <code>showVersion</code> very simply as</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">LibraryA</span></span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">LibraryA.Safe</span></span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a><span class="ot">main ::</span> <span class="dt">IO</span> ()</span>
<span id="cb9-5"><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a>main <span class="ot">=</span> showVersion <span class="op">$</span> <span class="dt">Version</span> <span class="dv">2</span> <span class="dv">3</span></span></code></pre></div>
<p>We will come back to the <code>HasField</code> instances for <code>Ptr Version</code> below; no
explicit <code>HasField</code> instances for <code>Version</code> itself are necessary, because they
are generated by <code>ghc.</code></p>
<h2 id="dependencies">Dependencies</h2>
<p>Suppose library B defines some kind of API for drivers, and suppose it <em>uses</em>
library A:</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="pp">#include </span><span class="im">&quot;library_a.h&quot;</span></span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> Driver <span class="op">{</span></span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a>  <span class="dt">char</span><span class="op">*</span> name<span class="op">;</span></span>
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true" tabindex="-1"></a>  <span class="kw">struct</span> Version version<span class="op">;</span></span>
<span id="cb10-6"><a href="#cb10-6" aria-hidden="true" tabindex="-1"></a><span class="op">};</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 class="dt">void</span> initDriver<span class="op">(</span><span class="kw">struct</span> Driver <span class="op">*</span>d<span class="op">);</span></span>
<span id="cb10-9"><a href="#cb10-9" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> showDriver<span class="op">(</span><span class="kw">struct</span> Driver <span class="op">*</span>d<span class="op">);</span></span></code></pre></div>
<h3 id="main-headers">Main headers</h3>
<p>If we run <code>hs-bindgen</code> on this header in the same way as we did for
<code>library_a.h</code>, we will get warnings such as</p>
<pre><code>[Warning] [HsBindgen] [select] 'struct Driver' at &quot;../cbits/library_b.h 8:8&quot;
  Could not select declaration (direct select predicate match):
    Transitive dependency not selected:
      'struct Version' at &quot;../cbits/library_a.h 3:8&quot;
      Adjust the select predicate or enable program slicing</code></pre>
<p>When we generate bindings for a header, we need to know which declarations in
that header the user wants to generate bindings for; in <code>hs-bindgen</code> this
happens by means of <em>selection predicates</em>. The default selection predicate is
<code>--select-from-main-headers</code>, which means that any declarations in headers
explicitly mentioned on the command line are selected, but any declarations in
headers that might be <em>imported by</em> those headers are not. The first way in
which we can fix this warning therefore is by explicitly generate bindings for
both <code>library_a.h</code> <em>and</em> <code>library_b.h</code>:</p>
<pre><code>cabal run -- hs-bindgen-cli preprocess \
  (..other arguments as before..)
  --module LibraryB \
  -I &quot;$(pwd)/cbits&quot; library_a.h library_b.h</code></pre>
<h3 id="program-slicing">Program slicing</h3>
<p>Suppose we are really only interested in library B, and want to generate only
those bindings in library A that are <em>required by</em> library B. To do this,
we can enable program slicing:</p>
<pre><code>cabal run -- hs-bindgen-cli preprocess \
  (..)
  --enable-program-slicing \
  -I &quot;$(pwd)/cbits&quot; library_b.h</code></pre>
<p>This will pull in only those declarations in library A that are referenced
by library B.</p>
<h3 id="opaque-types">Opaque types</h3>
<p>The API provided by library B exclusively works with <em>pointers</em> to <code>struct Driver</code>; so perhaps we don’t need a Haskell-side representation of that <code>struct</code>
at all. If that is the case, we can configure <code>hs-bindgen</code> through a
<em>prescriptive binding specification</em>, and tell it that it should keep the
Haskell type opaque:</p>
<div class="sourceCode" id="cb14"><pre class="sourceCode yaml"><code class="sourceCode yaml"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a><span class="fu">version</span><span class="kw">:</span></span>
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a><span class="at">  </span><span class="fu">hs_bindgen</span><span class="kw">:</span><span class="at"> </span><span class="fl">0.1.0</span></span>
<span id="cb14-3"><a href="#cb14-3" aria-hidden="true" tabindex="-1"></a><span class="at">  </span><span class="fu">binding_specification</span><span class="kw">:</span><span class="at"> </span><span class="st">'1.0'</span></span>
<span id="cb14-4"><a href="#cb14-4" aria-hidden="true" tabindex="-1"></a><span class="fu">ctypes</span><span class="kw">:</span></span>
<span id="cb14-5"><a href="#cb14-5" aria-hidden="true" tabindex="-1"></a><span class="kw">-</span><span class="at"> </span><span class="fu">headers</span><span class="kw">:</span><span class="at"> library_b.h</span></span>
<span id="cb14-6"><a href="#cb14-6" aria-hidden="true" tabindex="-1"></a><span class="at">  </span><span class="fu">cname</span><span class="kw">:</span><span class="at"> struct Driver</span></span>
<span id="cb14-7"><a href="#cb14-7" aria-hidden="true" tabindex="-1"></a><span class="at">  </span><span class="fu">hsname</span><span class="kw">:</span><span class="at"> Driver</span></span>
<span id="cb14-8"><a href="#cb14-8" aria-hidden="true" tabindex="-1"></a><span class="fu">hstypes</span><span class="kw">:</span></span>
<span id="cb14-9"><a href="#cb14-9" aria-hidden="true" tabindex="-1"></a><span class="kw">-</span><span class="at"> </span><span class="fu">hsname</span><span class="kw">:</span><span class="at"> Driver</span></span>
<span id="cb14-10"><a href="#cb14-10" aria-hidden="true" tabindex="-1"></a><span class="at">  </span><span class="fu">representation</span><span class="kw">:</span><span class="at"> emptydata</span></span></code></pre></div>
<p>This states that the C declaration <code>struct Driver</code>, found in header
<code>library_b.h</code>, should be mapped to a Haskell type called <code>Driver</code> (we could pick
a different name here if we wanted to, overriding naming decisions made by
<code>hs-bindgen</code>), and that the Haskell type <code>Driver</code> be represented as an empty datatype.
If we now run</p>
<pre><code>cabal run -- hs-bindgen-cli preprocess \
  (..)
  --prescriptive-binding-spec libraryB.yaml \
  -I &quot;$(pwd)/cbits&quot; library_b.h</code></pre>
<p>then the generated <code>LibraryB</code> is simply</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="kw">data</span> <span class="dt">Driver</span></span></code></pre></div>
<p>It can be quite useful to combine <code>emptydata</code> with program slicing, limiting
how many declarations from imported headers are in fact needed.</p>
<h2 id="composability">Composability</h2>
<p>The solutions in the previous section all had one important downside: in none of
them the generated code for library B reused the generated code for library A.
This kind of <em>composability</em> of generated bindings is an important goal of
<code>hs-bindgen</code>, influencing many design decisions. Composability is achieved
through (external) <em>binding specifications</em>; a binding specification is a
<code>.yaml</code> (or <code>.json</code>) file describing a set of generated bindings, a bit like
<code>.hi</code> files in Haskell, or a module signature in
<a href="https://ocaml.org/manual/5.4/moduleexamples.html#s%3Asignature">OCaml</a> or
<a href="https://wiki.haskell.org/Module_signature">Backpack</a>.</p>
<p>When we generate the bindings for library A we can ask <code>hs-bindgen</code> to
additionally generate a binding spec:</p>
<pre><code>cabal run -- hs-bindgen-cli preprocess \
  (..)
  --gen-binding-spec libraryA.yaml</code></pre>
<p>The resulting <code>.yaml</code> file describes the types generated for library A,
including which type class instances they have (necessary in order to know
which type class instances we can generate when these types are used in other
libraries). When generating bindings for library B, we can pass this binding
specification along:</p>
<pre><code>cabal run -- hs-bindgen-cli preprocess \
  (..)
  --external-binding-spec libraryA.yaml \
  -I &quot;$(pwd)/cbits&quot; library_b.h</code></pre>
<p>The generated code then looks like</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">module</span> <span class="dt">LibraryB</span> <span class="kw">where</span></span>
<span id="cb19-2"><a href="#cb19-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb19-3"><a href="#cb19-3" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="kw">qualified</span> <span class="dt">LibraryA</span></span>
<span id="cb19-4"><a href="#cb19-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb19-5"><a href="#cb19-5" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">Driver</span> <span class="ot">=</span> <span class="dt">Driver</span> {</span>
<span id="cb19-6"><a href="#cb19-6" aria-hidden="true" tabindex="-1"></a><span class="ot">    name    ::</span> <span class="dt">Ptr</span> <span class="dt">CChar</span></span>
<span id="cb19-7"><a href="#cb19-7" aria-hidden="true" tabindex="-1"></a>  ,<span class="ot"> version ::</span> <span class="dt">LibraryA.Version</span></span>
<span id="cb19-8"><a href="#cb19-8" aria-hidden="true" tabindex="-1"></a>  }</span>
<span id="cb19-9"><a href="#cb19-9" aria-hidden="true" tabindex="-1"></a>  <span class="kw">deriving</span> stock (<span class="dt">Eq</span>, <span class="dt">Show</span>)</span>
<span id="cb19-10"><a href="#cb19-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb19-11"><a href="#cb19-11" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">Storable</span> <span class="dt">Driver</span> <span class="kw">where</span> (<span class="op">..</span>)</span>
<span id="cb19-12"><a href="#cb19-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb19-13"><a href="#cb19-13" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">HasField</span> <span class="st">&quot;name&quot;</span>    (<span class="dt">Ptr</span> <span class="dt">Driver</span>) (<span class="dt">Ptr</span> (<span class="dt">Ptr</span> <span class="dt">CChar</span>))      <span class="kw">where</span> (<span class="op">..</span>)</span>
<span id="cb19-14"><a href="#cb19-14" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">HasField</span> <span class="st">&quot;version&quot;</span> (<span class="dt">Ptr</span> <span class="dt">Driver</span>) (<span class="dt">Ptr</span> <span class="dt">LibraryA.Version</span>) <span class="kw">where</span> (<span class="op">..</span>)</span></code></pre></div>
<p>External binding specifications are useful not only when generating bindings for
multiple libraries, but also for structuring bindings for multiple headers of
the same library, or when an identical header is included in lots of libraries
(such as the <code>rtwtypes.h</code> header generated by MATLAB). We consider external
binding specifications to be an essential feature of <code>hs-bindgen</code>.</p>
<h2 id="pointers">Pointers</h2>
<h3 id="hasfield">HasField</h3>
<p>Suppose we want to override one value deeply nested in some C data structure. We
<em>could</em> use the <code>Storable</code> instances to <code>peek</code> the value, then override the
appropriate field, and finally <code>poke</code> the updated value:</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="ot">main ::</span> <span class="dt">IO</span> ()</span>
<span id="cb20-2"><a href="#cb20-2" aria-hidden="true" tabindex="-1"></a>main <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb20-3"><a href="#cb20-3" aria-hidden="true" tabindex="-1"></a>    alloca <span class="op">$</span> \(<span class="ot">driverPtr ::</span> <span class="dt">Ptr</span> <span class="dt">Driver</span>) <span class="ot">-&gt;</span> <span class="kw">do</span></span>
<span id="cb20-4"><a href="#cb20-4" aria-hidden="true" tabindex="-1"></a>      initDriver driverPtr</span>
<span id="cb20-5"><a href="#cb20-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb20-6"><a href="#cb20-6" aria-hidden="true" tabindex="-1"></a>      driver <span class="ot">&lt;-</span> peek driverPtr</span>
<span id="cb20-7"><a href="#cb20-7" aria-hidden="true" tabindex="-1"></a>      poke driverPtr <span class="op">$</span> driver <span class="op">&amp;</span> <span class="op">#</span>version <span class="op">%</span> <span class="op">#</span>minor <span class="op">.~</span> <span class="dv">2</span></span>
<span id="cb20-8"><a href="#cb20-8" aria-hidden="true" tabindex="-1"></a>      showDriver driverPtr</span></code></pre></div>
<p>(The use of lenses here is optional of course.)</p>
<p>However, it may well be undesirable to marshall the entire structure back and
forth merely to change a single value. This is why <code>hs-bindgen</code> also generates
<code>HasField</code> instances for <em>pointers</em>, so that record dot syntax can be used
to index <em>C</em> structures. We can update the minor version number without
marshalling the entire structure as follows:</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>poke driverPtr<span class="op">.</span>version<span class="op">.</span>minor <span class="dv">3</span></span></code></pre></div>
<p>If you prefer to avoid record-dot syntax, you can use the
<a href="https://hackage-content.haskell.org/package/hs-bindgen-runtime-0.1.0/candidate/docs/HsBindgen-Runtime-HasCField.html"><code>HsBindgen.Runtime.HasCField</code></a>
infrastructure directly.</p>
<h3 id="funptr">FunPtr</h3>
<p>When dealing with a higher order API, <code>hs-bindgen</code> will generate additional
bindings to convert back and forth between C function pointers and Haskell
functions, and package these conversions up as instances of two type classes in
<code>hs-bindgen-runtime</code>:</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="kw">class</span> <span class="dt">ToFunPtr</span> a <span class="kw">where</span></span>
<span id="cb22-2"><a href="#cb22-2" aria-hidden="true" tabindex="-1"></a><span class="ot">  toFunPtr ::</span> a <span class="ot">-&gt;</span> <span class="dt">IO</span> (<span class="dt">FunPtr</span> a)</span>
<span id="cb22-3"><a href="#cb22-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb22-4"><a href="#cb22-4" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> <span class="dt">FromFunPtr</span> a <span class="kw">where</span></span>
<span id="cb22-5"><a href="#cb22-5" aria-hidden="true" tabindex="-1"></a><span class="ot">  fromFunPtr ::</span> <span class="dt">FunPtr</span> a <span class="ot">-&gt;</span> a</span>
<span id="cb22-6"><a href="#cb22-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb22-7"><a href="#cb22-7" aria-hidden="true" tabindex="-1"></a><span class="ot">withFunPtr ::</span> <span class="dt">ToFunPtr</span> a <span class="ot">=&gt;</span> a <span class="ot">-&gt;</span> (<span class="dt">FunPtr</span> a <span class="ot">-&gt;</span> <span class="dt">IO</span> b) <span class="ot">-&gt;</span> <span class="dt">IO</span> b</span></code></pre></div>
<p>For example, suppose the driver API in library B additionally contains</p>
<div class="sourceCode" id="cb23"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb23-1"><a href="#cb23-1" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> Driver<span class="op">;</span></span>
<span id="cb23-2"><a href="#cb23-2" aria-hidden="true" tabindex="-1"></a><span class="kw">typedef</span> <span class="dt">int</span> RunDriver<span class="op">(</span><span class="kw">struct</span> Driver<span class="op">*</span> self<span class="op">);</span></span>
<span id="cb23-3"><a href="#cb23-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb23-4"><a href="#cb23-4" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> Driver <span class="op">{</span></span>
<span id="cb23-5"><a href="#cb23-5" aria-hidden="true" tabindex="-1"></a>  <span class="co">// .. other fields as before ..</span></span>
<span id="cb23-6"><a href="#cb23-6" aria-hidden="true" tabindex="-1"></a>  RunDriver<span class="op">*</span> run<span class="op">;</span></span>
<span id="cb23-7"><a href="#cb23-7" aria-hidden="true" tabindex="-1"></a><span class="op">};</span></span>
<span id="cb23-8"><a href="#cb23-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb23-9"><a href="#cb23-9" aria-hidden="true" tabindex="-1"></a><span class="dt">int</span> callDriver<span class="op">(</span><span class="kw">struct</span> Driver<span class="op">*</span> d<span class="op">);</span></span></code></pre></div>
<p>then we generate</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">newtype</span> <span class="dt">RunDriver</span> <span class="ot">=</span> <span class="dt">RunDriver</span> {</span>
<span id="cb24-2"><a href="#cb24-2" aria-hidden="true" tabindex="-1"></a><span class="ot">    unwrap ::</span> <span class="dt">Ptr</span> <span class="dt">Driver</span> <span class="ot">-&gt;</span> <span class="dt">IO</span> <span class="dt">CInt</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></span>
<span id="cb24-5"><a href="#cb24-5" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">ToFunPtr</span>   <span class="dt">RunDriver</span> <span class="kw">where</span> (<span class="op">..</span>)</span>
<span id="cb24-6"><a href="#cb24-6" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">FromFunPtr</span> <span class="dt">RunDriver</span> <span class="kw">where</span> (<span class="op">..</span>)</span></code></pre></div>
<p>Here’s how we might use this:</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>counter <span class="ot">&lt;-</span> newIORef <span class="dv">0</span></span>
<span id="cb25-2"><a href="#cb25-2" aria-hidden="true" tabindex="-1"></a><span class="kw">let</span> run <span class="ot">=</span> <span class="dt">RunDriver</span> <span class="op">$</span> \_self <span class="ot">-&gt;</span> atomicModifyIORef counter <span class="op">$</span> \x <span class="ot">-&gt;</span> (<span class="fu">succ</span> x, x)</span>
<span id="cb25-3"><a href="#cb25-3" aria-hidden="true" tabindex="-1"></a>withFunPtr run <span class="op">$</span> \funPtr <span class="ot">-&gt;</span> <span class="kw">do</span></span>
<span id="cb25-4"><a href="#cb25-4" aria-hidden="true" tabindex="-1"></a>  poke driverPtr<span class="op">.</span>run funPtr</span>
<span id="cb25-5"><a href="#cb25-5" aria-hidden="true" tabindex="-1"></a>  replicateM_ <span class="dv">5</span> <span class="op">$</span> <span class="fu">print</span> <span class="op">=&lt;&lt;</span> callDriver driverPtr</span></code></pre></div>
<h2 id="macros">Macros</h2>
<p>Some C libraries make part of their API available as CPP macros. Macros in C
don’t have a semantics per se; they are merely a list of tokens that the
preprocessor splices in whenever they are used. In order to nonetheless be able
to generate “bindings” for macros, <code>hs-bindgen</code> imbues macros with a bespoke
semantics. In future versions of <code>hs-bindgen</code> this macro infrastructure
will be pluggable (<a href="https://github.com/well-typed/hs-bindgen/issues/942">#942</a>),
because the default semantics may not suit all applications.</p>
<h3 id="constants">Constants</h3>
<p>Low level C libraries (such as this Analog Devices <a href="https://github.com/analogdevicesinc/linux/blob/b7484c97688ddd60bfd05a1d6d28f454f3f0dbb0/drivers/iio/adc/talise/talise_arm_macros.h#L48-L55">Talise</a> driver)
may make certain constants such as bitfields available as CPP macros</p>
<div class="sourceCode" id="cb26"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb26-1"><a href="#cb26-1" aria-hidden="true" tabindex="-1"></a><span class="pp">#define SIGNALID </span><span class="bn">0x01</span></span></code></pre></div>
<p>We translate these to Haskell constants:</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="ot">sIGNALID ::</span> <span class="dt">CInt</span></span>
<span id="cb27-2"><a href="#cb27-2" aria-hidden="true" tabindex="-1"></a>sIGNALID <span class="ot">=</span> <span class="dv">1</span></span></code></pre></div>
<h3 id="expressions">Expressions</h3>
<p>It may also happen that libraries offer certain functionality as macro
<em>functions</em>. For example, a library might provide a definition that
provides a pointer offset for devices with memory-mapped I/O:</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="pp">#define INPUT_PORT(x) x + 4</span></span></code></pre></div>
<p>Since <code>hs-bindgen</code> has no way of knowing if that <code>(+)</code> operator should be
interpreted as integer addition or pointer offset (or indeed something else),
it generates a very general definition:</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="ot">iNPUT_PORT ::</span> <span class="dt">Add</span> a <span class="dt">CInt</span> <span class="ot">=&gt;</span> a <span class="ot">-&gt;</span> <span class="dt">AddRes</span> a <span class="dt">CInt</span></span></code></pre></div>
<p><code>Add</code> and <code>AddRes</code> come from <a href="https://hackage.haskell.org/package/c-expr-runtime-0.1.0.0/candidate"><code>c-expr-runtime</code></a>); one way that we can instantiate this type is to:</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="ot">inputPort ::</span> <span class="dt">Ptr</span> <span class="dt">Word8</span> <span class="ot">-&gt;</span> <span class="dt">Ptr</span> <span class="dt">Word8</span></span>
<span id="cb30-2"><a href="#cb30-2" aria-hidden="true" tabindex="-1"></a>inputPort <span class="ot">=</span> iNPUT_PORT</span></code></pre></div>
<h3 id="types">Types</h3>
<p>Finally, some low-level libraries define <em>types</em> as macros:</p>
<div class="sourceCode" id="cb31"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb31-1"><a href="#cb31-1" aria-hidden="true" tabindex="-1"></a><span class="pp">#define S16_TYPE </span><span class="dt">short</span><span class="pp"> </span><span class="dt">int</span></span></code></pre></div>
<p>In the case (and only in the case) that these can be parsed as the corresponding
typedef</p>
<div class="sourceCode" id="cb32"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb32-1"><a href="#cb32-1" aria-hidden="true" tabindex="-1"></a><span class="kw">typedef</span> <span class="dt">short</span> <span class="dt">int</span> S16_TYPE<span class="op">;</span></span></code></pre></div>
<p><code>hs-bindgen</code> will treat these <em>as</em> <code>typedefs</code>, and generate a Haskell newtype:</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="kw">newtype</span> <span class="dt">S16_TYPE</span> <span class="ot">=</span> <span class="dt">S16_TYPE</span> {</span>
<span id="cb33-2"><a href="#cb33-2" aria-hidden="true" tabindex="-1"></a><span class="ot">    unwrap ::</span> <span class="dt">CShort</span></span>
<span id="cb33-3"><a href="#cb33-3" aria-hidden="true" tabindex="-1"></a>  }</span></code></pre></div>
<h2 id="squashing">Squashing</h2>
<p>C <code>struct</code>s are often defined using a <code>typedef</code>:</p>
<div class="sourceCode" id="cb34"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb34-1"><a href="#cb34-1" aria-hidden="true" tabindex="-1"></a><span class="kw">typedef</span> <span class="kw">struct</span> Point <span class="op">{</span></span>
<span id="cb34-2"><a href="#cb34-2" aria-hidden="true" tabindex="-1"></a>  <span class="dt">int</span> x<span class="op">;</span></span>
<span id="cb34-3"><a href="#cb34-3" aria-hidden="true" tabindex="-1"></a>  <span class="dt">int</span> y<span class="op">;</span></span>
<span id="cb34-4"><a href="#cb34-4" aria-hidden="true" tabindex="-1"></a><span class="op">}</span> Point<span class="op">;</span></span></code></pre></div>
<p>This is typically done for syntactic convenience only, making it possible to
write simply <code>Point</code> rather than <code>struct Point</code>. When the <em>only</em> use of a struct
is within a <code>typedef</code> in this manner, <code>hs-bindgen</code> will “squash” the <code>typedef</code>,
and generate a single type only:</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="kw">data</span> <span class="dt">Point</span> <span class="ot">=</span> <span class="dt">Point</span> {</span>
<span id="cb35-2"><a href="#cb35-2" aria-hidden="true" tabindex="-1"></a><span class="ot">    x ::</span> <span class="dt">CInt</span></span>
<span id="cb35-3"><a href="#cb35-3" aria-hidden="true" tabindex="-1"></a>  ,<span class="ot"> y ::</span> <span class="dt">CInt</span></span>
<span id="cb35-4"><a href="#cb35-4" aria-hidden="true" tabindex="-1"></a>  }</span></code></pre></div>
<p>If however the <code>typedef</code> is <em>not</em> the only use of the <code>struct</code>, then
<code>hs-bindgen</code> assumes that this is intended to convey some kind of semantic
information, and will not squash. For example, suppose the device driver API
includes</p>
<div class="sourceCode" id="cb36"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb36-1"><a href="#cb36-1" aria-hidden="true" tabindex="-1"></a><span class="kw">typedef</span> <span class="kw">struct</span> Driver DeviceDriver<span class="op">;</span></span></code></pre></div>
<p>then we generate</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="kw">newtype</span> <span class="dt">DeviceDriver</span> <span class="ot">=</span> <span class="dt">DeviceDriver</span> {</span>
<span id="cb37-2"><a href="#cb37-2" aria-hidden="true" tabindex="-1"></a><span class="ot">    unwrap ::</span> <span class="dt">Driver</span></span>
<span id="cb37-3"><a href="#cb37-3" aria-hidden="true" tabindex="-1"></a>  }</span></code></pre></div>
<p>To avoid confusion, a notice will be emitted whenever a type is squashed. If
desired, this notice can be suppressed with
<code>--log-as-info select-mangle-names-squashed</code>.
In a future release of <code>hs-bindgen</code> squashing will be configurable per <code>typedef</code>
using a prescriptive binding spec
(<a href="https://github.com/well-typed/hs-bindgen/issues/1436">#1436</a>).</p>
<h2 id="build-process-integration">Build process integration</h2>
<h3 id="preprocessor">Preprocessor</h3>
<p>The primary way of invoking <code>hs-bindgen</code> is by calling <code>hs-bindgen-cli</code>, as we
have discussed in this blogpost, raising the question of how to integrate that
into your build process. We don’t have a perfect answer here just yet. You
can of course write your own <code>Makefile</code>, or integrate this into a <code>nix</code>
derivation (see the <a href="https://github.com/well-typed/hs-bindgen-tutorial-nix/">Nix tutorial</a>). If you want to use <code>cabal</code> as the main driver, you can use a custom
setup script, or the new
<a href="https://hackage-content.haskell.org/package/Cabal-hooks-3.16/docs/Distribution-Simple-SetupHooks.html#g:6"><code>SetupHooks</code></a>
API; we don’t provide explicit support for either just yet
(<a href="https://github.com/well-typed/hs-bindgen/issues/1666">#1666</a> tracks adding
support for <code>SetupHooks</code>).</p>
<p>We do offer a “literate” mode, abusing Cabal’s support for literate Haskell to
trick it into running <code>hs-bindgen</code> instead; see section
<a href="https://github.com/well-typed/hs-bindgen/blob/main/manual/LowLevel/Usage/01-Invocation.md#cabal-preprocessor-integration">Cabal preprocessor integration</a>
of the <code>hs-bindgen</code> manual for details.</p>
<h3 id="template-haskell">Template Haskell</h3>
<p>If you prefer, you can use <code>hs-bindgen</code> in TH mode; this avoids the need for
running the preprocessor altogether. Instead, you can
<code>#include</code> a C header in a Haskell module:</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="kw">let</span><span class="ot"> cfg ::</span> <span class="dt">Config</span></span>
<span id="cb38-2"><a href="#cb38-2" aria-hidden="true" tabindex="-1"></a>    cfg <span class="ot">=</span> def <span class="op">&amp;</span> <span class="op">#</span>clang <span class="op">%</span> <span class="op">#</span>extraIncludeDirs <span class="op">.~</span> [<span class="dt">Pkg</span> <span class="st">&quot;cbits&quot;</span>]</span>
<span id="cb38-3"><a href="#cb38-3" aria-hidden="true" tabindex="-1"></a> <span class="kw">in</span> withHsBindgen cfg def <span class="op">$</span></span>
<span id="cb38-4"><a href="#cb38-4" aria-hidden="true" tabindex="-1"></a>       hashInclude <span class="st">&quot;library_a.h&quot;</span></span></code></pre></div>
<p>Since we cannot generate more than one module in this way, by default this
splices in bindings for all the types in the header along with the <em>safe</em>
imports, though you can override that if you wish. Of course, you can also
use <code>hashInclude</code> in more than one Haskell module to manually generate multiple
modules. See <a href="https://github.com/well-typed/hs-bindgen/blob/main/manual/LowLevel/Usage/01-Invocation.md#template-haskell-mode">Template Haskell
mode</a>
in the manual for details.</p>
<h3 id="cross-compilation">Cross-compilation</h3>
<p>We rely on <a href="https://clang.llvm.org/doxygen/group__CINDEX.html"><code>libclang</code></a> for
all machine-dependent decisions (as well as parsing the C headers in the first
place). Therefore if you want to generate code for a target differing from your host
platform, in principle it should suffice to provide the appropriate <code>clang</code>
arguments. We are working on a guide for doing so; this will become section
<a href="https://github.com/well-typed/hs-bindgen/blob/main/manual/LowLevel/Usage/08-CrossCompilation.md">Cross-compilation</a>
in the manual; however, this section is not quite ready yet
(<a href="https://github.com/well-typed/hs-bindgen/pull/1630">#1630</a>).</p>
<h3 id="non-portability">Non-portability</h3>
<p>It is important to note that the bindings generated by <code>hs-bindgen</code> are <em>not</em>
portable in general (indeed, it will be rare that they are). The slogan to
remember is:</p>
<blockquote>
The bindings generated by <code>hs-bindgen</code> should be regarded as build artefacts.
</blockquote>
<p>If you <em>do</em> want to distribute generated bindings as part of your package, you
can of course do so, but then you are responsible for making the appropriate
provisions in your <code>.cabal</code> file (perhaps using <code>SetupHooks</code>) to check machine
architecture, choose between different sets of bindings, etc. At present
<code>hs-bindgen</code> does not yet provide any explicit support for making this process
easier.</p>
<h2 id="conclusions">Conclusions</h2>
<p>Although Haskell provides good FFI features, writing bindings to large C
libraries by hand can be a laborious process. The bindings generated by
<code>hs-bindgen</code> are low-level: <code>char*</code> is translated to <code>Ptr CChar</code>, not
<code>ByteString</code> or <code>Text</code> or something else still; nonetheless, it should make the
process of writing bindings <em>significantly</em> easier. Automatically generating
<em>high-level</em> bindings is something we’ll soon turn our attention to.</p>
<p>Before we do so, however, there is still some
<a href="https://github.com/well-typed/hs-bindgen/issues?q=is%3Aissue%20state%3Aopen%20label%3Arelease-0.1">cleanup to be done</a>
before we can release version 0.1. As it stands, the alpha release supports
nearly all of C, although some missing corner-cases are still missing (such
as implicit fields,
<a href="https://github.com/well-typed/hs-bindgen/issues/1649">#1649</a>); see the issue
tracker for a
<a href="https://github.com/well-typed/hs-bindgen/issues?q=is%3Aissue%20state%3Aopen%20label%3Amissing-c-feature">list of missing C features</a>.
However, we would like to invite you to start using the alpha release now; there
should only be very minor backwards incompatible changes introduced in between
versions <code>0.1-alpha</code> and <code>0.1</code>, so your code should not break when upgrading to version <code>0.1</code>.</p>
<h3 id="acknowledgements">Acknowledgements</h3>
<p>Many people at Well-Typed have contributed to <code>hs-bindgen</code>, including Travis
Cardwell, Joris Dral, Dominik Schrempf, Armando Santos, Sam Derbyshire, and
others (as well as myself, Edsko de Vries).</p>
<p>Well-Typed is grateful to Anduril Industries for sponsoring this work.</p>
<h3 id="further-reading">Further reading</h3>
<ul>
<li>Paper on <code>hs-bindgen</code>: <a href="https://well-typed.com/blog/aux/files/haskell2025-hs-bindgen.pdf">Automatic C Bindings Generation for Haskell</a>, published at <a href="https://dl.acm.org/doi/10.1145/3759164.3759350">Haskell 2025</a>. You might also like to <a href="https://www.youtube.com/watch?v=SeLI3lMnhnA">watch the presentation</a>.</li>
<li>The <a href="https://github.com/well-typed/hs-bindgen/tree/main/manual"><code>hs-bindgen</code> manual</a>. Note that while many sections of the manual have already been written, some are still empty or out of date; this is one of the things that will be completed prior to the official 0.1 release.</li>
<li>The <a href="https://github.com/well-typed/hs-bindgen-tutorial-nix"><code>hs-bindgen nix tutorial</code></a>. This tutorial shows how to generate partial bindings for two libraries (<code>pcap</code> and <code>wlroots</code>). Although this assumes <code>nix</code>, most of the information here will be relevant for non-<code>nix</code> users also.</li>
<li>The <a href="https://github.com/well-typed/hs-bindgen/tree/main/examples"><code>examples</code></a> folder of the <code>hs-bindgen</code> repo, which contains example partial bindings for a bunch of libraries, such as <a href="https://botan.randombit.net/"><code>Botan</code></a>, <a href="https://github.com/niklasso/minisat-c-bindings"><code>minisat</code></a>, <a href="https://github.com/nayuki/QR-Code-generator"><code>QR-Code-generator</code></a>, <a href="https://github.com/sakhmatd/rogueutil"><code>rogueutil</code></a>, <a href="https://github.com/rpm-software-management/rpm"><code>rpm</code></a> and <a href="https://github.com/the-tcpdump-group/libpcap"><code>libpcap</code></a>.</li>
</ul>
<section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes">
<hr />
<ol>
<li id="fn1"><p>The C namespace is a global namespace, so when <code>hs-bindgen</code>
introduces new C symbols, they must also be globally unique. Normally the
uniqueness of the symbols introduced by <code>hs-bindgen</code> is derived from the
uniqueness of the C symbols for which we are generating bindings, but it may happen
that multiple Haskell libraries include bindings for the <em>same</em> C function. In
such a case it is important that the <code>--unique-id</code> used for these different
Haskell modules is different.<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
</ol>
</section>
]]></content:encoded>
</item>
<item>
    <title>The Haskell Debugger for GHC 9.14</title>
    <link>https://well-typed.com/blog/2026/01/haskell-debugger</link>
    <description><![CDATA[The Haskell Debugger is ready to use with GHC-9.14!

The installation, configuration, and talks can be found in the official website. The tl;dr first step is installing the debugger:

``` bash
$ ghc --version # MUST BE GHC 9.14
The Glorious Glasgow Haskell Compilation System, version 9.14.1

$ cabal [...]]]></description>
    <dc:creator>rodrigo</dc:creator>
    <category>debugging</category>
    <category>ghc</category>
    <category>open-source</category>
    <category>community</category>
    <guid isPermaLink="false">http://www.well-typed.com/blog/2026/01/haskell-debugger</guid>
    <pubDate>Thu, 08 Jan 2026 00:00:00 GMT</pubDate>
    <content:encoded><![CDATA[<p><strong>The Haskell Debugger is ready to use with GHC-9.14!</strong></p>
<p>The installation, configuration, and talks can be found in <a href="https://well-typed.github.io/haskell-debugger">the official website</a>. The tl;dr first step is installing the debugger:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="ex">$</span> ghc <span class="at">--version</span> <span class="co"># MUST BE GHC 9.14</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="ex">The</span> Glorious Glasgow Haskell Compilation System, version 9.14.1</span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="ex">$</span> cabal install haskell-debugger <span class="at">--allow-newer</span><span class="op">=</span>base,time,containers,ghc,ghc-bignum,template-haskell <span class="at">--enable-executable-dynamic</span> <span class="co"># ON WINDOWS, DO NOT PASS --enable-executable-dynamic</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a><span class="ex">...</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a><span class="ex">$</span> ~/.local/bin/hdb <span class="at">--version</span> <span class="co"># VERIFY IT'S THE LATEST!</span></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a><span class="ex">Haskell</span> Debugger, version 0.11.0.0</span></code></pre></div>
<p>The second step is configuring your editor to use the debugger via the <strong>D</strong>ebug <strong>A</strong>dapter <strong>P</strong>rotocol (DAP).</p>
<ul>
<li>For VSCode, install <a href="https://marketplace.visualstudio.com/items?itemName=Well-Typed.haskell-debugger-extension">the haskell debugger extension</a>.</li>
<li>For Neovim, install <code>nvim-dap</code> and <a href="https://codeberg.org/mfussenegger/nvim-dap/wiki/Debug-Adapter-installation#haskell-hdb">configure it for haskell-debugger</a></li>
<li>For other editors, consult your DAP documentation and let others know how!</li>
</ul>
<p>Bug reports and discussions are welcome in the <a href="https://github.com/well-typed/haskell-debugger">haskell-debugger issue tracker</a>.</p>
<p>My MuniHac 2025 talk also walks through the installation, usage, and design of the debugger. Do note much has been improved since the talk was given, and much more will still improve.</p>
<div style="width: 560px; height: 315px; margin-bottom: 1em" data-service="youtube" data-id="urYtE15ryA0" data-thumbnail="../../../../img/youtube-thumbnails/urYtE15ryA0.jpg" data-autoscale>

</div>
<h4 id="a-little-bit-more-info">A little bit more info</h4>
<!-- more -->
<p>The debugger work is sponsored by Mercury. It’s the project in which I’ve spent most of my full working days (for almost a full year now), with the invaluable help from my team at Well-Typed.</p>
<p>The debugger is meant to work both on trivial files and on large and complex codebases.<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a> It is a GHC application so <em>all features are supported</em>. Like HLS, it also uses <code>hie-bios</code> to automatically configure the session based on your cabal or stack project.</p>
<p>Robustness is a main goal of the debugger. If anything doesn’t work, or if you have performance issues, or something crashes, please don’t hesitate to submit a bug. We’ve got a small but respectable testsuite, and have tested performance by debugging GHC itself, but there’s much still to be fixed and improved.</p>
<p><strong>Roadmap</strong>: There’s a lot to do. I’m currently working on callstacks and multi-threaded support. Do let me know what features would be most important to you, so I can also factor that into the future planning.</p>
<section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes">
<hr />
<ol>
<li id="fn1"><p>Although, for large codebases, the usability is still rough around the edges because of possibly long bytecode compilation times, and library code not being interpreted. We’ve made considerable progress to improve this with the <a href="https://discourse.haskell.org/t/rfc-introduce-a-serialisable-bytecode-format-and-corresponding-bytecode-way/12678">bytecode artifacts work</a>.<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
</ol>
</section>
]]></content:encoded>
</item>
<item>
    <title>Haskell ecosystem activities report: September–November 2025</title>
    <link>https://well-typed.com/blog/2025/12/haskell-ecosystem-report-september-november-2025</link>
    <description><![CDATA[This is the twenty-ninth 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
September 2025 to November 2025.

You can find the previous editions [...]]]></description>
    <dc:creator>adam, andreask, hannes, magnus, matthew, mikolaj, rodrigo, sam, 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/2025/12/haskell-ecosystem-report-september-november-2025</guid>
    <pubDate>Fri, 19 Dec 2025 00:00:00 GMT</pubDate>
    <content:encoded><![CDATA[<p>This is the twenty-ninth 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
September 2025 to November 2025.</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="team">Team</h2>
<p>Ben, who has been a key maintainer of GHC for many years,
<a href="https://discourse.haskell.org/t/taking-on-a-new-role-in-the-ghc-ecosystem/13114">announced in October</a>
that he is moving to a new role that will allow less time for GHC work,
though he remains a Partner at Well-Typed. We were delighted to welcome Magnus
to the team in November.</p>
<!-- more -->
<p>The Haskell toolchain team at Well-Typed currently includes:</p>
<ul>
<li><a href="https://well-typed.com/people/andreask">Andreas Klebinger</a></li>
<li><a href="https://well-typed.com/people/hannes">Hannes Siebenhandl</a></li>
<li><a href="https://well-typed.com/people/magnus">Magnus Viernickel</a></li>
<li><a href="https://well-typed.com/people/matthew">Matthew Pickering</a></li>
<li><a href="https://well-typed.com/people/mikolaj">Mikolaj Konarski</a></li>
<li><a href="https://well-typed.com/people/rodrigo">Rodrigo Mesquita</a></li>
<li><a href="https://well-typed.com/people/sam">Sam Derbyshire</a></li>
<li><a href="https://well-typed.com/people/zubin">Zubin Duggal</a></li>
</ul>
<p>In addition, many others within Well-Typed contribute to GHC, Cabal and HLS
occasionally, or contribute to other open source Haskell libraries and tools.
This report includes contributions from <a href="https://well-typed.com/people/alex">Alex Washburn</a>, <a href="https://well-typed.com/people/tobias">Tobias
Dammers</a>, <a href="https://well-typed.com/people/wen">Wen Kokke</a> and <a href="https://well-typed.com/people/wolfgang">Wolfgang Jeltsch</a> in
particular.</p>
<h2 id="ghc">GHC</h2>
<h3 id="ghc-releases">GHC Releases</h3>
<ul>
<li>Ben worked on backports for GHC 9.12.3. Zubin continued this work and
released rc1 and rc2.</li>
<li>Ben worked on backports for GHC 9.14.1, and released alpha1, alpha2,
alpha3 and rc1. Zubin continued this work and released rc2 and rc3.</li>
<li>An <a href="https://gitlab.haskell.org/ghc/ghc/-/wikis/GHC%20Status">overview of the status of GHC releases</a> is available on the GHC wiki.</li>
</ul>
<h3 id="reinstallable-base">Reinstallable Base</h3>
<ul>
<li><p>Matthew has facilitated a period of <a href="https://github.com/haskell/core-libraries-committee/issues/375">community
discussion</a>
about plans for making the <code>base</code> library reinstallable, in order to reduce
the coupling between GHC upgrades and library version upgrades. This
discussion aims to assess what the consensus is about the project and to
establish a shared plan amongst all the stakeholders.</p></li>
<li><p>Wolfgang and Tobias have been investigating the technical aspects of
reinstallable <code>base</code>, including questions such as what concretely it takes to
compile an API-identical <code>base</code> with multiple GHC versions, what the testing
story for an external base repository would be and which parts of
<code>ghc-internal</code> can be moved back to <code>base</code>.</p></li>
</ul>
<h3 id="frontend">Frontend</h3>
<ul>
<li><p>Sam added error message hints for unsolved <code>HasField</code> constraints. GHC will now
provide additional hints for an unsolved constraint of the form
<code>HasField fld_name rec_ty fld_ty</code>, such as similar name suggestions for
misspelled field names, warnings about record fields that contain existentials
or foralls, import suggestions, etc. This is particularly useful for users of
the <code>OverloadedRecordDot</code> extension (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/18776">#18776</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/22382">#22382</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26480">#26480</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14952">!14952</a>).</p></li>
<li><p>Magnus worked on fixing several oversights with the initial implementation of
<code>ExplicitLevelImports</code>, such as re-instating an erroneously removed level check
(<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26099">#26099</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15053">!15053</a>) and addressing a module graph issue (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26568">#26568</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15118">!15118</a>). He
also took the opportunity to better document that area of the codebase
(<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15117">!15117</a>).</p></li>
<li><p>Sam reworked the way error messages involving invisible components (e.g. the
<code>k</code> in <code>Proxy @k ty</code>, or <code>RuntimeRep</code> variables as in <code>TYPE r</code>) are reported,
bringing the pretty-printing of multiplicities (from <code>LinearTypes</code>) in line
with other invisible components (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26335">#26335</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26340">#26340</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14761">!14761</a>).</p></li>
<li><p>Sam cleaned up how GHC pulls out <code>TypeError msg</code> when computing insoluble
constraints. This makes the code much more consistent, but also improves
detection of redundant pattern matches as in <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26400">#26400</a> (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14802">!14802</a>).</p></li>
<li><p>Ben added a special case to avoid GHC emitting “unused package” warnings for
packages which don’t contain any modules, as such packages are typically used
for other purposes such as providing linker flags (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/24120">#24120</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14965">!14965</a>).</p></li>
<li><p>Sam reviewed Vladislav Zavialov’s work implementing <code>type ..</code> and
<code>data ..</code> namespace-specified wildcards from <a href="https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/0581-namespace-specified-imports.rst">GHC proposal <span>#581</span></a> (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15014">!15014</a>).</p></li>
</ul>
<h4 id="type-checker-plugins">Type-checker plugins</h4>
<ul>
<li><p>Sam made GHC invoke type-checker plugins when running the pattern-match
checker. This greatly improves pattern match warnings when using type-checker
plugins and dealing with GADTs that involve natural numbers (such as <code>Vec ::   Nat -&gt; Type -&gt; Type</code>) (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26395">#26395</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14797">!14797</a>).</p></li>
<li><p>Sam added an <code>Outputable Natural</code> instance in the <code>ghc</code> library, useful in
particular for type-checking plugins dealing with natural numbers.</p></li>
</ul>
<h4 id="representation-polymorphism">Representation polymorphism</h4>
<ul>
<li><p>Sam improved the representation-polymorphism checks to allow function
applications in which the representation of the argument is hidden under a
type family reduction (such as in <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26072">#26072</a>). As part of this work, Sam undertook
a major overhaul of the typechecking of data constructors as it relates to
linear types. The end result is that GHC no longer eta-expands saturated
applications of data constructors, resulting in reduced compilation times
(<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14357">!14357</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14764">!14764</a>).</p></li>
<li><p>Sam fixed an incorrect coercion constructed during representation-polymorphism
checking (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26528">#26528</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15038">!15038</a>).</p></li>
</ul>
<h4 id="other-frontend-bug-fixes">Other frontend bug fixes</h4>
<ul>
<li><p>Matthew improved error handling in the driver when processing linking nodes,
resolving some failures in the <code>T9930fail</code> test (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14964">!14964</a>).</p></li>
<li><p>Sam fixed a GHC panic that could occur when writing <code>Arrow</code>/<code>Category</code> code
in which overloaded code is instantiated to the function arrow <code>(-&gt;)</code> (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26277">#26277</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14760">!14760</a>).</p></li>
<li><p>Sam reverted <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14402">!14402</a> in order to fix GHC failing to compile Agda (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26154">#26154</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14763">!14763</a>).</p></li>
<li><p>Sam fixed a GHC panic that could occur when reporting an invalid record update
in which the record constructor was out of scope (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26391">#26391</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14804">!14804</a>).</p></li>
<li><p>Sam fixed a GHC panic that could occur when typechecking a pattern synonym
that uses a view pattern (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26465">#26465</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14936">!14936</a>).</p></li>
</ul>
<h3 id="backend">Backend</h3>
<ul>
<li><p>Matthew improved the memory usage of recompilation checking by using <code>OsPath</code>
in the <code>getModificationTimeIfExists</code> function (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14917">!14917</a>).</p></li>
<li><p>With the help of Andreas, Sam identified and fixed a long standing serious bug
in the Cmm Sink optimisation pass, which is responsible for inlining variable
declarations at the Cmm level (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26550">#26550</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15041">!15041</a>).</p></li>
<li><p>Sam implemented several fixes relating to register allocation, in particular
in relation to tracking formats of registers for the purpose of spilling to
the stack (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26542">#26542</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15041">!15041</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26411">#26411</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15121">!15121</a>).</p></li>
<li><p>Sam fixed an oversight in the Cmm liveness analysis pass in which GHC did not
properly combine register allocations at different formats (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26595">#26595</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26611">#26611</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15121">!15121</a>).</p></li>
<li><p>Zubin re-engineered the tags used for <code>Unique</code>s within GHC, making GHC use
a proper ADT rather than ad-hoc characters to namespace <code>Unique</code>s (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26246">#26246</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14639">!14639</a>).
This also makes it clearer to plugin authors which tag to use when generating
<code>Unique</code>s.</p></li>
<li><p>Andreas investigated the performance of occurrence analysis, adding a new
performance test from <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26425">#26425</a> (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14927">!14927</a>). Several improvements were then made,
such as avoiding space leaks by being a bit more strict (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14928">!14928</a>).</p></li>
<li><p>Andreas added handling of ticks in the <code>sizeExpr</code> computation (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14883">!14883</a>).</p></li>
<li><p>Andreas added a regression test for <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26056">#26056</a> (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14719">!14719</a>), which had been fixed by
his earlier work on the interaction of ticks with <code>unsafeCoerce#</code> (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/13413">!13413</a>).</p></li>
<li><p>Matthew made the loading of bytecode static pointer entries more lazy (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14900">!14900</a>).</p></li>
</ul>
<h3 id="ghci-and-bytecode-interpreter">GHCi and bytecode interpreter</h3>
<ul>
<li><p>Matthew added support for bytecode libraries, allowing the bytecode for
standard libraries (such as <code>base</code>) to be distributed, which in turns allows
the GHCi debugger to step through these libraries (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15062">!15062</a>).</p></li>
<li><p>Matthew added support in GHC for generating bytecode object files (<code>.gbc</code>).
This allows compiled bytecode to be cached, improving load times of the
interpreter (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14717">!14717</a>).</p></li>
<li><p>Matthew refactored the code involved in loading bytecode in order to unify
two code-paths with a lot of duplication (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26459">#26459</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14901">!14901</a>).</p></li>
<li><p>Alex worked on issues with the reliance of the bytecode linker on laziness
(<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/25636">#25636</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15092">!15092</a>).</p></li>
<li><p>Rodrigo improved the performance of the bytecode interpreter by pruning
certain useless sequences of instructions (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14576">!14576</a>).</p></li>
<li><p>Rodrigo changed the breakpoint index from <code>Word16</code> to <code>Word32</code> in order to
fix <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26325">#26325</a> (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14691">!14691</a>).</p></li>
<li><p>Matthew renamed, within the GHC codebase, the “interpreter backend” to the
“bytecode backend”, which names what the backend produces rather than
the means by which the code will eventually run (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14915">!14915</a>).</p></li>
</ul>
<h3 id="trees-that-grow">Trees That Grow</h3>
<ul>
<li><p>Alex has been working on fully realising the vision introduced by the
<a href="https://simon.peytonjones.org/assets/pdfs/trees-that-grow.pdf">“Trees that Grow” (TTG) paper</a>
by making the syntax tree used by GHC fully independent of GHC. This aims at
making GHC more modular and potentially making it easier for other Haskell
tools to reuse GHC’s syntax tree without acquiring a dependency on the full
<code>ghc</code> package.</p></li>
<li><p>Alex decoupled <code>Language.Haskell.Syntax.Type</code> from depending on
<code>GHC.Utils.Panic</code> (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26626">#26626</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15134">!15134</a>), and initiated preliminary work to decouple
other modules in the <code>Language.Haskell</code> namespace from GHC internal modules.</p></li>
</ul>
<h3 id="runtime-system-and-linker">Runtime system and linker</h3>
<ul>
<li><p>With the help of Andreas, Zubin identified and fixed a segfault that would
occur on Windows due to DLL name strings getting freed while they were still
used (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26613">#26613</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15100">!15100</a>).</p></li>
<li><p>Ben added dynamic initialisation of built-in RTS closures in order to
address MacOS 26 breakage of <code>-undefined dynamic_lookup</code> (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26166">#26166</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14902">!14902</a>).</p></li>
<li><p>Rodrigo moved certain symbols from <code>ghc-internal</code> to the <code>rts</code> package, as part
of the effort to address the aforementioned MacOS 26 breakage (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14956">!14956</a>).</p></li>
<li><p>Ben improved how the RTS deals with address space reservations in low memory,
avoiding potential loops (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26151">#26151</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14466">!14466</a>).</p></li>
<li><p>Matthew fixed a deadlock that could occur when the RTS was shutting down while
trying to emit eventlogs (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26573">#26573</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15068">!15068</a>).</p></li>
<li><p>Ben fixed a runtime loop that could occur with the non-moving garbage
collector by using atomic operations (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26053">#26053</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14519">!14519</a>).</p></li>
<li><p>Rodrigo dropped some obsolete logic in the RTS relating to Windows DLLs
(<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14873">!14873</a>).</p></li>
<li><p>Hannes fixed an off-by-one error in an assertion in the RTS (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15019">!15019</a>).</p></li>
</ul>
<h3 id="profiling">Profiling</h3>
<ul>
<li><p>Hannes enabled the stack frame annotation mechanisms by default for IPE
backtraces (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26218">#26218</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14721">!14721</a>).</p></li>
<li><p>Hannes moved the implementation of stack decoding logic from <code>ghc-heap</code> to
<code>ghc-internal</code>, in order to allow its usage in <code>GHC.Stack.CloneStack</code> in
<code>base</code> (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14543">!14543</a>).</p></li>
<li><p>Matthew implemented several fixes to the stack decoding logic when using the
profiled runtime (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26507">#26507</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14991">!14991</a>).</p></li>
<li><p>Building on previous work by Finley McIlwaine, Hannes introduced the
<code>-fno-distinct-constructor-tables</code> and <code>-fdistinct-constructor-tables-only</code>
flags which provide more granular control over which constructors get
different info tables when using info-table profiling (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/23703">#23703</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14705">!14705</a>). This
is useful as <code>-fdistinct-constructor-tables</code> can lead to unreasonably large
binary sizes in some projects.</p></li>
</ul>
<h3 id="template-haskell">Template Haskell</h3>
<ul>
<li><p>Ben improved the documentation of <code>getQ</code> from the <code>template-haskell</code> library
to better reflect its semantics (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26484">#26484</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14970">!14970</a>).</p></li>
<li><p>Sam improved the Template Haskell compatibility story for the new “expressions
in specialise pragmas” patch by using a pattern synonym to avoid breaking
existing Template Haskell code (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26356">#26356</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14729">!14729</a>).</p></li>
</ul>
<h3 id="build-system-and-packaging">Build system and packaging</h3>
<ul>
<li><p>Andreas fixed a bug with long paths on Windows when invoking GHC to gather
module dependency information (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15060">!15060</a>).</p></li>
<li><p>Rodrigo made further progress on removing global settings files in favour
of per-target configuration (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26227">#26227</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14554">!14554</a>).</p></li>
<li><p>Rodrigo ensures that flags such as <code>CFLAGS</code> apply only to the build phase, and
are not carried over to target settings (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/25637">#25637</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14854">!14854</a>).</p></li>
<li><p>Rodrigo deleted some stale settings from the <code>Hadrian</code> bindist configure
script (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26478">#26478</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14937">!14937</a>).</p></li>
<li><p>Ben fixed <code>zstd</code>-enabled builds (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26312">#26312</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14693">!14693</a>).</p></li>
<li><p>Ben fixed inconsistencies in <code>configure</code> checks relating to <code>C</code> compilers
(<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26394">#26394</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14796">!14796</a>).</p></li>
<li><p>Ben fixed a build failure caused by Clang warning on implicit constant arrays
(<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26502">#26502</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14971">!14971</a>).</p></li>
<li><p>Ben made a change to the order of arguments when building with Hadrian
(<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/25281">#25281</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14318">!14318</a>). Thanks to Cheng Shao for helping land this work.</p></li>
<li><p>Andreas added <code>hpc</code> to the release script (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15056">!15056</a>).</p></li>
</ul>
<h3 id="ci-and-testing">CI and testing</h3>
<ul>
<li><p>In an ongoing general effort to improve CI stability in nightly pipelines,
Magnus fixed several issues with the CI to <a href="https://grafana.gitlab.haskell.org/d/ab109e66-a8a1-4ae9-b976-40e2dfe281ab/availability-of-ghc-nightlies-via-ghcup?orgId=2">make nightly GHC builds available
again</a>.
This included fixing the Debian 9 CI job, which was failing due to a small
Python issue (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15127">!15127</a>).</p></li>
<li><p>Andreas improved the <code>jspace</code> test to be less flaky, fixing a recurrent issue
with AArch64 LLVM jobs in CI (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/25401">#25401</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14837">!14837</a>).</p></li>
<li><p>Zubin added support for testsuite output files that depend on the testsuite way,
fixing test failures that used the external interpreter (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26552">#26552</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/15078">!15078</a>).</p></li>
<li><p>Zubin addressed an issue in which spurious linker warnings were causing
testsuite failures (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26349">#26349</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14615">!14615</a>).</p></li>
<li><p>Zubin fixed the links to <code>-fno-xxx</code> flags in the user’s guide (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26352">#26352</a>,<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14794">!14794</a>).</p></li>
<li><p>Sam fixed a Unix vs Windows filepath issue with the <code>codes</code> test (which checks
test coverage of error codes). This makes the test more robust on Windows
(<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/25178">#25178</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14778">!14778</a>).</p></li>
<li><p>Ben made whether a job is a release job into a GitLab “input”, facilitating
release management for maintainers (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14933">!14933</a>).</p></li>
<li><p>Ben disabled split sections on FreeBSD in order to fix CI failures on FreeBSD
(<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26303">#26303</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14782">!14782</a>).</p></li>
<li><p>Matthew made some changes to the testsuite driver to allow some tests to
run only with GHCi (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14914">!14914</a>).</p></li>
<li><p>Rodrigo made some progress on fixing the <code>test-reinstall</code> jobs which tests
compilation of GHC using <code>cabal</code> (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26202">#26202</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14518">!14518</a>).</p></li>
</ul>
<h3 id="infrastructure">Infrastructure</h3>
<ul>
<li><p>Ben migrated <code>ghcup-metadata</code> jobs to run on the new <code>OpenCape</code> CI runners
(<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14814">!14814</a>).</p></li>
<li><p>Magnus set up new Windows x86 runners and improved stability and observability of the GHC GitLab instance.</p></li>
</ul>
<h2 id="haddock">Haddock</h2>
<ul>
<li>Sam made several changes to the information stored in interface files for
data constructors, in order to improve Haddock output. This allows Haddock to
display the user-written kind signature (instead of aggressively expanding
type synonyms), as well as preserve user-written type variable names (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26246">#26246</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14868">!14868</a>).</li>
</ul>
<h2 id="cabal">Cabal</h2>
<ul>
<li><p>Mikolaj took part in the preparation of the upcoming Cabal release 3.16.1.0.</p></li>
<li><p>Matthew helped on the Cabal issue tracker and reviewed PRs from other Cabal contributors.</p></li>
<li><p>Matthew wrote up <a href="https://github.com/haskell/cabal-proposals/pull/2">a Cabal proposal</a>
to add support for bytecode library files and bytecode object files.</p></li>
<li><p>Matthew fixed two issues reported in <a href="https://github.com/haskell/cabal/issues/11107"><span>#11107</span></a>
that occur when starting an interactive session with <code>cabal repl</code> (<a href="https://github.com/haskell/cabal/pull/11237">PR <span>#11237</span></a>).</p></li>
</ul>
<h2 id="haskell-language-server">Haskell Language Server</h2>
<ul>
<li><p>Zubin prepared the release of HLS 2.12.0.0 (<a href="https://github.com/haskell/haskell-language-server/pull/4728">PR <span>#4728</span></a>).</p></li>
<li><p>Hannes fixed a bug in which GHC plugins aren’t loaded for files that are
checked in the background (<a href="https://github.com/haskell/haskell-language-server/issues/4631"><span>#4631</span></a>, <a href="https://github.com/haskell/haskell-language-server/pull/4749">PR <span>#4749</span></a>).</p></li>
<li><p>Hannes and Andreas diagnosed a recurrent segfault that turned out to be
caused by how the <code>ghc-lib-parser</code> library would re-use shared CAFs defined
in the RTS but expecting a different memory layout due to API changes in
between GHC versions (<a href="https://github.com/haskell/haskell-language-server/issues/4674">HLS issue <span>#4674</span></a>, GHC tickets <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26033">#26033</a> and <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26553">#26553</a>).</p></li>
</ul>
<h2 id="haskell-debugger">Haskell Debugger</h2>
<ul>
<li><p>Rodrigo continued work on the new <a href="https://well-typed.github.io/haskell-debugger/">Haskell Debugger</a>.
The debugger will first be compatible with the upcoming GHC 9.14 release, and we’ll be looking forward to you all trying it out.</p></li>
<li><p>Rodrigo presented the debugger at MuniHac in a tutorial oriented for users.
The <a href="https://www.youtube.com/watch?v=urYtE15ryA0">recording is available here</a>.</p></li>
<li><p>A detailed list of improvements to the debugger can be found <a href="https://github.com/well-typed/haskell-debugger/blob/master/CHANGELOG.md">in the changelog</a>. Notably:</p>
<ul>
<li>Several robustness fixes and bug fixes</li>
<li>Support for stepping-out of functions</li>
<li>Conditional and hit conditional breakpoints</li>
<li>Proxy program which runs on the terminal to allow passing input to the debuggee</li>
</ul></li>
<li><p>Matthew and Rodrigo worked on allowing customisation of how the debugger displays user values.
By instantiating the <code>DebugView</code> type class for your data type, you can control how
the debugger expands and displays the value in the debugger.</p></li>
</ul>
<h2 id="live-monitoring-using-the-eventlog">Live monitoring using the eventlog</h2>
<ul>
<li><p>Wen has been working on improvements to
<a href="https://github.com/well-typed/eventlog-live/"><code>eventlog-live</code></a>, a collection
of libraries and tools for the live profiling of Haskell applications using
the <a href="https://github.com/well-typed/eventlog-socket/"><code>eventlog-socket</code></a>
library to stream data from the eventlog to an external monitoring process.</p></li>
<li><p>Matthew has implemented support for creating TCP sockets and increased the testing
coverage of the <code>eventlog-socket</code> library (<a href="https://github.com/well-typed/eventlog-socket/pull/18">PR <span>#18</span></a>).</p></li>
<li><p>The socket created can now be used bidirectionally using a control protocol to
instruct the monitored process for further heap profiling information (<a href="https://github.com/well-typed/eventlog-socket/pull/20">PR <span>#20</span></a>). In future work, the protocol will be extended to allow other libraries
to register commands.</p></li>
</ul>
]]></content:encoded>
</item>
<item>
    <title>Lazy Linearity for a Core Functional Language accepted at POPL 2026</title>
    <link>https://well-typed.com/blog/2025/11/lazy-linearity-popl26</link>
    <description><![CDATA[We're delighted to announce that Rodrigo's paper on the
theory for extending GHC Core to cover linear types has been accepted at POPL
2026, and will be presented in January. Read more background on Rodrigo's
blog, or check out the extended version of the paper on arXiv!

  Lazy Linearity for a Core Functional [...]]]></description>
    <dc:creator>rodrigo, adam</dc:creator>
    <category>paper</category>
    <category>ghc</category>
    <category>community</category>
    <category>linear-types</category>
    <category>popl</category>
    <guid isPermaLink="false">http://www.well-typed.com/blog/2025/11/lazy-linearity-popl26</guid>
    <pubDate>Fri, 28 Nov 2025 00:00:00 GMT</pubDate>
    <content:encoded><![CDATA[<p>We’re delighted to announce that <a href="https://well-typed.com/people/rodrigo">Rodrigo</a>’s paper on the
theory for extending GHC Core to cover linear types has been accepted at POPL
2026, and will be presented in January. Read more background on <a href="https://alt-romes.github.io/posts/2025-11-26-lazy-linearity-popl26.html">Rodrigo’s
blog</a>, or check out the <a href="https://arxiv.org/abs/2511.10361">extended version of the paper on arXiv</a>!</p>
<blockquote>
<p><strong>Lazy Linearity for a Core Functional Language</strong></p>
<p><em>Rodrigo Mesquita, Bernardo Toninho</em></p>
<p>POPL 2026 (<a href="https://arxiv.org/pdf/2511.10361">PDF</a>)</p>
<p><strong>Abstract.</strong> Traditionally, in linearly typed languages, consuming a linear
resource is synonymous with its syntactic occurrence in the program. However,
under the lens of non-strict evaluation, linearity can be further understood
semantically, where a syntactic occurrence of a resource does not necessarily
entail using that resource when the program is executed. While this
distinction has been largely unexplored, it turns out to be inescapable in
Haskell’s optimising compiler, which heavily rewrites the source program in
ways that break syntactic linearity but preserve the program’s semantics. We
introduce Linear Core, a novel system which accepts the lazy semantics of
linearity statically and is suitable for lazy languages such as the Core
intermediate language of the Glasgow Haskell Compiler. We prove that Linear
Core is sound, guaranteeing linear resource usage, and that multiple
optimising transformations preserve linearity in Linear Core while failing to
do so in Core. We have implemented Linear Core as a compiler plugin to
validate the system against linearity-heavy libraries, including linear-base.</p>
</blockquote>
]]></content:encoded>
</item>
<item>
    <title>Case Study: Debugging a Haskell space leak</title>
    <link>https://well-typed.com/blog/2025/10/case-study-debugging-space-leak</link>
    <description><![CDATA[As part of their Haskell Ecosystem Support Package, QBayLogic asked us to investigate
a space leak in one of their Haskell applications, a simulation of a circuit using Clash.
The starting point was a link to a ticket in the bittide-hardware package with reproduction instructions.

This post explains [...]]]></description>
    <dc:creator>matthew</dc:creator>
    <category>ghc-debug</category>
    <category>ecosystem</category>
    <category>debugging</category>
    <category>memory</category>
    <category>profiling</category>
    <category>performance</category>
    <guid isPermaLink="false">http://www.well-typed.com/blog/2025/10/case-study-debugging-space-leak</guid>
    <pubDate>Fri, 31 Oct 2025 00:00:00 GMT</pubDate>
    <content:encoded><![CDATA[<p>As part of their <a href="https://well-typed.com/ecosystem">Haskell Ecosystem Support Package</a>, QBayLogic asked us to investigate
a space leak in one of their Haskell applications, a simulation of a circuit using <a href="https://clash-lang.org/">Clash</a>.
The starting point was a link to <a href="https://github.com/bittide/bittide-hardware/issues/784">a ticket in the <code>bittide-hardware</code> package</a> with reproduction instructions.</p>
<p>This post explains the debugging process which led to the resolution of this
ticket. At the start of the investigation the program used 2 GB memory, at the
end, about 200 MB, an improvement of approximately 10x!</p>
<h2 id="first-impressions">First impressions</h2>
<!-- more -->
<p>I first looked at the ticket report to get an idea of the problem.</p>
<ul>
<li>The ticket contained a profile generated by <a href="https://mpickering.github.io/eventlog2html/"><code>eventlog2html</code></a> which
showed a profile of a program which runs in two phases. During these two
phases the memory increased before resetting to some baseline and then increasing again.</li>
<li>Reproduction instructions were provided in the subsequent comment, I could run
these easily to reproduce the issue. I altered the options to use the <code>-hT</code> profiling
mode to generate a basic heap profile without needing to compile with profiling support. This is a useful technique to
get an initial handle on the problem.</li>
<li>The instructions used <code>profiling-detail: all-functions</code>, which will insert many cost centres into the program which
will significantly affect the runtime characteristics of the resulting program. I replaced
this with <a href="https://well-typed.com/blog/2023/03/prof-late/"><code>profiling-detail: late</code></a>.</li>
</ul>
<p>Most importantly, the ticket lacked a precise description about what the issue
was with the profile. It may have been that this was exactly the memory profile
that the program should exhibit! When starting to think about memory issues,
thinking about memory invariants is a very helpful technique. The first question
I ask myself is:</p>
<blockquote>
<p>What is the memory invariant that the program should uphold?</p>
</blockquote>
<p>This situation was a useful test of this technique, since I had no domain knowledge
of what the program did, what the test did or what function the library even aimed
to perform. It certainly highlighted to me the importance of knowing your domain and
knowing the invariants.</p>
<h3 id="memory-invariants">Memory invariants</h3>
<p>A memory invariant is a property that your program’s heap should obey.
Establishing a memory invariant makes it easier to verify and fix memory
issues, since if you have a precise invariant, it is easy to check whether the
invariant holds.</p>
<p>A memory invariant consists of two parts:</p>
<ul>
<li>A predicate on the heap</li>
<li>The timeline over which the predicate should hold</li>
</ul>
<p>For example, some predicates on the heap might be:</p>
<ul>
<li>“No constructors of type T are alive”</li>
<li>“There is an upper bound of 20000 bytestrings”</li>
<li>“There are exactly 5 live closures of type T”</li>
<li>“No closures of type T are reachable from closures of type Q”</li>
</ul>
<p>When paired with a timeline, a memory invariant is formed.
Example timelines include:</p>
<ul>
<li>“<strong>Before</strong> the second phase of the program”</li>
<li>“<strong>During</strong> the cleanup phase”</li>
<li>“<strong>After</strong> initialisation”</li>
<li>“<strong>Between</strong> runs 10 and 100”</li>
<li>“<strong>At all points</strong> of the program’s execution”</li>
</ul>
<p>Establishing a memory invariant requires domain knowledge about the program. Without
first establishing an invariant (even informally in your head), you can’t begin to
debug memory usage of a program. The main challenge for me when investigating this
issue was coming up with a memory invariant.</p>
<h2 id="initial-investigation">Initial investigation</h2>
<p>In order to get an idea of how to proceed, I generated a “Profile by Closure Type” using
the <code>-hT</code> runtime system option.</p>
<pre><code>cabal run bittide-instances:unittests -- -p RegisterWb +RTS -hT -l -RTS</code></pre>
<p>The result was a <code>unittests.eventlog</code> file which contains the profiling
information.</p>
<p>I rendered this eventlog using <code>eventlog2html</code> and inspected the result in my
browser.</p>
<pre><code>eventlog2html unittests.eventlog</code></pre>
<p>The profile shows a coarse breakdown by the type of closures currently alive
on the heap. The maximum value reported in the profile is about 600 MB. This value
relates to the total memory used by the process (2 GB), but doesn’t include additional
memory used by the RTS. The relationship between live bytes and OS memory
usage is explained fully in <a href="https://well-typed.com/blog/2021/03/memory-return#inspecting-memory-usage">this blog post</a>.
Reducing live memory is a good way to reduce the overall memory usage of your program,
but it isn’t the only factor.</p>
<p>The top four bands came from</p>
<ul>
<li><code>Clash.Signal.Internal.:-</code></li>
<li><code>Protocols.Wishbone.Wishbone.S2M</code></li>
<li><code>THUNK_1_0</code></li>
<li>2-tuples <code>(,)</code></li>
</ul>
<p><img src="https://well-typed.com/blog/aux/images/qbay-memory-leak/hT-initial-overview.png" /></p>
<p>and as can be easily seen from the “detailed pane”, the patterns of allocation
of these top four bands closely align with each other:</p>
<p><img src="https://well-typed.com/blog/aux/images/qbay-memory-leak/hT-initial-detailed.png" /></p>
<p>Looking at these correlations in the detailed pane can be invaluable in understanding
the root issue, since memory issues are normally about different closures retaining each
other, they are allocated together and retained together. Seeing these overall
patterns can give you context about what exact kind of thing is using the memory.</p>
<p>Without a clear memory invariant, I wanted to get a better idea about
these top 4 bands of allocation, I had a hypothesis at this stage that the
<code>THUNK</code> closures were contained within tuples, which were retaining the <code>WishboneS2M</code> and <code>:-</code>
constructors.</p>
<h3 id="a-more-specific-profile-with-info-table-provenance">A more specific profile with info table provenance</h3>
<p>I want to know information about the precise source location of the <code>:-</code>, <code>WishboneS2M</code> constructors
and thunks. Therefore I enabled a few more debugging options to add this information
to the binary:</p>
<ul>
<li><code>-finfo-table-map</code>: Gives a source location to each thunk and data constructor</li>
<li><code>-fdistinct-constructor-tables</code>: Distinguishes between allocation sites of each constructor</li>
</ul>
<p>Then if you make a profile using the <code>-hi</code> option, you get a very fine-grained breakdown about
where exactly in your program to start looking. That’s useful for me since I didn’t
yet look at any of the source code!</p>
<pre><code>cabal run bittide-instances:unittests -- -p RegisterWb +RTS -hi -l -RTS</code></pre>
<figure>
<img src="https://well-typed.com/blog/aux/images/qbay-memory-leak/hi-initial-detailed.png" alt="Nothing very useful from these source locations." />
<figcaption aria-hidden="true"><a href="https://well-typed.com/blog/aux/images/qbay-memory-leak/hi-initial-detailed.png">Nothing very useful from these source locations.</a></figcaption>
</figure>
<p>After consulting the source code in the relevant places, I quickly realised
that this wouldn’t necessarily be as straightforward an investigation as I had hoped.
I had hoped that the <code>THUNK_1_0</code> locations reported in the <code>-hT</code> profile would
be clear that my hypothesis about retaining was correct, but the <code>-hi</code> profile didn’t
show up anything directly wrong. These locations were normal ways you could construct
a Clash circuit.</p>
<p>At this stage my lack of a memory invariant or some domain knowledge was a hindrance.
I took the opportunity to consult Ben who knew about the Clash ecosystem
and asked on the ticket what the expected profile should look like.</p>
<ul>
<li>The program is simulating a digital circuit.</li>
<li>The <code>:-</code> constructor represents a single time-step of simulation.</li>
<li>The expected memory profile is to use a constant (or near constant) amount of memory,
since the circuit being simulated has bounded size.</li>
</ul>
<p>For this program, a plausible invariant might have been: the number of <code>:-</code>
constructors should remain roughly constant during simulation.</p>
<p>With this knowledge, the number of <code>:-</code> constructors alive seemed to be the biggest
unexpected source of memory usage. Knowing that <code>:-</code> is a data constructor with two
arguments,
Each allocation is 24 bytes, so 240 MB of live <code>:-</code> closures corresponds to
roughly 10 million constructors. That is certainly an issue.</p>
<p>Secondly, the number of live <code>WishboneS2M</code> constructors looked wrong. I didn’t have
a good idea of the domain still, but by similar arithmetic, many millions of these
are also resident on the heap.</p>
<p>These two facts gave me some further avenues to investigate but I was going to
need to use <code>ghc-debug</code> to investigate further.</p>
<h3 id="using-ghc-debug-to-investigate-retainers">Using <code>ghc-debug</code> to investigate retainers</h3>
<p>Using <code>ghc-debug</code> I wanted to establish</p>
<ul>
<li>What was retaining <code>:-</code> constructors</li>
<li>What was retaining <code>WishboneS2M</code> constructors</li>
</ul>
<p>Therefore I <a href="https://well-typed.com/blog/2021/01/fragmentation-deeper-look/#what-is-ghc-debug">instrumented the test executable</a>, and launched <code>ghc-debug-brick</code>
in order to query what was retaining <code>:-</code> and <code>WishboneS2M</code>.
This was the start of making progress on the investigation.</p>
<p>To find the retainers of <code>:-</code>, I paused the test program just after the test started and used the “Find retainers”
command in <code>ghc-debug-brick</code>. The result was a list of 100 <code>:-</code> closures, and when expanded,
each one shows the path which was taken to reach it. It wasn’t very important where I paused,
as long as it was in this initial period, since we saw in the profile that the <code>:-</code> closures are alive and
linearly increasing for the whole phase.</p>
<p>When looking at retainers of <code>:-</code>, it was immediately noticeable that
the program contained very long chains of <code>:-</code> constructors (upwards of 5000 long).
This looked wrong to me, since my understanding was that <code>:-</code> was being used as a control
operation to drive the simulation of the circuit.</p>
<p><img src="https://well-typed.com/blog/aux/images/qbay-memory-leak/long-chain-of-signal.png" /></p>
<p>The information about where each <code>:-</code> constructor was allocated is not very informative.
That just gives me a location inside the library functions.</p>
<p>The question then becomes, why is <code>:-</code> being retained? I scrolled, for a long while,
and eventually get to the point where the chain of <code>:-</code> constructors is retained by
a non-<code>:-</code> constructor. That’s the interesting part since it’s the part of the program
which led to the long chain being retained.</p>
<p><img src="https://well-typed.com/blog/aux/images/qbay-memory-leak/end-of-signal-constructors.png" /></p>
<p>At the time, I didn’t think this looked so interesting, but also I didn’t know what I
was looking for exactly.</p>
<p>So I kept going down the stack, looking for anything
which looked suspicious. In the end, I got quite lucky: I found a tuple which was
retained by a thunk. Since I had compiled with profiling enabled, I could see the cost
centre stack where the thunk was allocated, which pointed to the implementation of <code>singleMasterInterconnectC</code>.</p>
<p><img src="https://well-typed.com/blog/aux/images/qbay-memory-leak/initial-find.png" /></p>
<h2 id="culprit-1-lazy-unzip">Culprit 1: lazy <code>unzip</code></h2>
<p>In the source code of <code>singleMasterInterconnectC</code>, I worked out this part of the allocation was coming from
these calls to <code>unzip</code>.</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>  go (((), m2s), <span class="fu">unzip</span> <span class="ot">-&gt;</span> (prefixes, <span class="fu">unzip</span> <span class="ot">-&gt;</span> (slaveMms, s2ms))) <span class="ot">=</span> ((<span class="dt">SimOnly</span> memMap, s2m), (\x <span class="ot">-&gt;</span> ((),     ((), x))) <span class="op">&lt;$&gt;</span> m2ss)</span></code></pre></div>
<p>Then I looked at the definition of <code>unzip</code>, and found it was defined in a very lazy manner.</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><span class="fu">unzip</span><span class="ot"> ::</span> <span class="dt">Vec</span> n (a,b) <span class="ot">-&gt;</span> (<span class="dt">Vec</span> n a, <span class="dt">Vec</span> n b)</span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a><span class="fu">unzip</span> xs <span class="ot">=</span> (<span class="fu">map</span> <span class="fu">fst</span> xs, <span class="fu">map</span> <span class="fu">snd</span> xs)</span></code></pre></div>
<p>With this definition, the thunk created by applying <code>map</code> to <code>fst</code> and <code>xs</code> retains
a reference to <code>xs</code>, which retains a reference to all the <code>b</code>s as well as the <code>a</code>s.
In a definition which performs a single iteration, if you force either list, both
will be evaluated and leave no reference to the other half.
I changed this definition to one which performed a single
iteration and this had a massive positive effect on memory usage.</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="fu">unzip</span> xs</span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a>  <span class="op">|</span> clashSimulation <span class="ot">=</span> unzipSim xs</span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a>  <span class="op">|</span> <span class="fu">otherwise</span> <span class="ot">=</span> (<span class="fu">map</span> <span class="fu">fst</span> xs, <span class="fu">map</span> <span class="fu">snd</span> xs)</span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a> <span class="kw">where</span></span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a><span class="ot">  unzipSim ::</span> <span class="dt">Vec</span> m (a,b) <span class="ot">-&gt;</span> (<span class="dt">Vec</span> m a, <span class="dt">Vec</span> m b)</span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a>  unzipSim <span class="dt">Nil</span> <span class="ot">=</span> (<span class="dt">Nil</span>, <span class="dt">Nil</span>)</span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a>  unzipSim (<span class="op">~</span>(a,b) <span class="ot">`Cons`</span> rest) <span class="ot">=</span></span>
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a>    <span class="kw">let</span> (as, bs) <span class="ot">=</span> unzipSim rest</span>
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a>    <span class="kw">in</span> (a <span class="ot">`Cons`</span> as, b <span class="ot">`Cons`</span> bs)</span></code></pre></div>
<p>This issue was hard to spot with the tools, I got lucky, but it was harder to spot since
<code>unzip</code> was also marked as <code>INLINE</code>. In the end, I guessed right, but it’s a bit unsatisfying to
not have a great story about how I worked it out, but I knew the answer was somewhere
in the retainer stack I was looking at, and eventually I looked in the right place.</p>
<p>This problem is similar to one you can encounter when
using <a href="https://www.well-typed.com/blog/2016/09/sharing-conduit/"><code>conduit</code> and similar libraries</a>.
In short, by sharing a thunk between two consumers, the input
structure can be retained longer than intended. Since one part of the program continues by
evaluating the thunk, the other reference
is updated to the result of the thunk being evaluated. This is a problem though,
since the original structure was intended to be used for control and discarded
immediately after driving the next step of execution.</p>
<h2 id="culprit-2-lazy-record-update-retains-old-field-values">Culprit 2: lazy record update retains old field values</h2>
<p>Once the original problem <a href="https://github.com/clash-lang/clash-compiler/pull/3039">had been fixed</a>,
memory usage was much improved. I circled back to the start to look at a modified <code>-hT</code> profile.
Perhaps there were still other problems lurking?</p>
<p><img src="https://well-typed.com/blog/aux/images/qbay-memory-leak/hT-initial-fix.png" /></p>
<p>The final phase of memory usage looks much better, so I turned my attention to the initial phase.
In the initial phase, it looked like <code>OUTPUT</code> was increasing linearly.</p>
<p><img src="https://well-typed.com/blog/aux/images/qbay-memory-leak/hT-initial-output.png" /></p>
<p>I turned to
<code>ghc-debug</code> again, inspected the retainers of the <code>OUTPUT</code> constructor and discovered that
the fields of <code>WishboneM2S</code> were not being strictly updated, indirectly keeping a reference to
the <code>OUTPUT</code> constructor.</p>
<p><img src="https://well-typed.com/blog/aux/images/qbay-memory-leak/wishbone-thunk.png" /></p>
<p>I looked at the source location that <code>WishboneM2S</code> was allocated,</p>
<p><img src="https://well-typed.com/blog/aux/images/qbay-memory-leak/wishbone-location.png" /></p>
<p>and made the update strict:</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode diff"><code class="sourceCode diff"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a>     toSlaves =</span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a><span class="st">-      (\newStrobe -&gt; (updateM2SAddr newAddr master){strobe = strobe &amp;&amp; newStrobe})</span></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a><span class="va">+      (\newStrobe -&gt; strictM2S $ (updateM2SAddr newAddr master){strobe = strobe &amp;&amp; newStrobe})</span></span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a>         &lt;$&gt; oneHotOrZeroSelected</span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a>     toMaster</span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a>       | busCycle &amp;&amp; strobe =</span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a><span class="dt">@@ -152,10 +152,8 @@ singleMasterInterconnect (fmap pack -&gt; config) =</span></span>
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true" tabindex="-1"></a>             (maskToMaybes slaves oneHotOrZeroSelected)</span>
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true" tabindex="-1"></a>       | otherwise = emptyWishboneS2M</span>
<span id="cb7-10"><a href="#cb7-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-11"><a href="#cb7-11" aria-hidden="true" tabindex="-1"></a><span class="va">+    strictM2S (WishboneM2S !a !b !c !d !e !f !g !h !i) = WishboneM2S a b c d e f g h i</span></span></code></pre></div>
<p>This resulted in another reduction in memory usage:</p>
<p><img src="https://well-typed.com/blog/aux/images/qbay-memory-leak/hT-second-fix.png" /></p>
<h2 id="culprit-3-lack-of-sharing-in-iterate">Culprit 3: lack of sharing in <code>iterate</code></h2>
<p>When I returned to the profile a final time, the memory usage seemed much better,
especially in the initial section. I had now eliminated retained <code>:-</code> constructors,
so the overall memory usage was much lower, but still increasing slightly. I turned
to a <code>-hi</code> profile again to get more information about the <code>THUNK</code> bands.</p>
<p><img src="https://well-typed.com/blog/aux/images/qbay-memory-leak/hi-second-fix-detailed.png" /></p>
<p>Looking at the source code for the <code>sat_ssVQ</code> thunks, they come from the <code>Clash.Sized.Vector.map</code> function. Therefore…
back to <code>ghc-debug</code> to see what retains these <code>map</code> thunks, and the callstack where
they are allocated from. This time I used “Find Retainers (Exact)”, to find closures
which are named <code>sat_ssVQ_info</code>.</p>
<p>The first one I looked at, I found was allocated from <code>iterateI</code> by inspecting the cost
centre stack.</p>
<p><img src="https://well-typed.com/blog/aux/images/qbay-memory-leak/iterateI-ghc-debug.png" /></p>
<p><code>iterateI</code> was implemented as follows:</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="ot">iterateI ::</span> <span class="kw">forall</span> n a<span class="op">.</span> <span class="dt">KnownNat</span> n <span class="ot">=&gt;</span> (a <span class="ot">-&gt;</span> a) <span class="ot">-&gt;</span> a <span class="ot">-&gt;</span> <span class="dt">Vec</span> n a</span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a>iterateI f a <span class="ot">=</span> xs</span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a>  <span class="kw">where</span></span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a>    xs <span class="ot">=</span> <span class="fu">init</span> (a <span class="ot">`Cons`</span> ws)</span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a>    ws <span class="ot">=</span> <span class="fu">map</span> f (lazyV xs)</span></code></pre></div>
<p>Reasoning about the definition, you can see that <code>iterateI</code> will result in a vector
of the form:</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode hs"><code class="sourceCode haskell"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a>a <span class="ot">`Cons`</span> <span class="fu">map</span> f (a <span class="ot">`Cons`</span> (<span class="fu">map</span> f (a <span class="ot">`Cons`</span> <span class="op">...</span>)))</span></code></pre></div>
<p>As a result,
each element of the vector will independently compute <code>f^n a</code>, no intermediate results
will be shared, a quadratic amount of thunks for the <code>n</code> applications of <code>f</code> will
be allocated.</p>
<p>Defining <code>iterateI</code> in a directly recursive style means only a linear amount of
thunks will be allocated and <code>f</code> will be computed only a linear number of times.</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="ot">iterateU ::</span> <span class="dt">UNat</span> n <span class="ot">-&gt;</span> (a <span class="ot">-&gt;</span> a) <span class="ot">-&gt;</span> a <span class="ot">-&gt;</span> <span class="dt">Vec</span> n a</span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a>iterateU <span class="dt">UZero</span> _ _ <span class="ot">=</span> <span class="dt">Nil</span></span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a>iterateU (<span class="dt">USucc</span> s) f a <span class="ot">=</span> a <span class="ot">`Cons`</span> iterateU s f (f a)</span></code></pre></div>
<p>Even better, for the specific example, was to use a strict accumulator,
so no intermediate thunks were allocated or retained in the early part of the program.</p>
<h2 id="final-result">Final result</h2>
<p>The final profile shows slightly higher memory usage in the initial phase of the program’s execution than the second phase, but
looking at the detailed pane, I could identify why the memory was retained.</p>
<p><img src="https://well-typed.com/blog/aux/images/qbay-memory-leak/final-hi-profile.png" /></p>
<p>Overall, the total memory usage decreased from about 2 GB to 200 MB.
There is probably still some improvement which can be made to this profile, but we felt
like it was a good place to stop for this post.</p>
<h2 id="conclusion">Conclusion</h2>
<p>The goal of this post is to document the thought process involved in investigating
a memory issue. Overall, I feel like I would have found it easier to fix the problem
with some domain knowledge. Once I acquired some knowledge about the domain I made
much more rapid progress about what to investigate.</p>
<p>The main cause of the memory leak was not obvious, and I got a bit lucky in finding the right place. One issue
being the problem was obfuscated by the problematic definition being inlined. In general, with
a busy heap, finding the needle can be quite tricky. Debugging
the subsequent issues was more straightforward.</p>
<p>In the future, we want to explore more reliable ways to identify and investigate
the kinds of memory invariants that were violated in this program. For example,
it was crucial to know that <code>:-</code> should not be retained, perhaps additional language
design can express that property more clearly. On another note, a logical specification
of memory invariants could be useful to automatically detect and pause the program at
the exact point a violation was detected.
There remains significant potential to improve our memory debugging tooling!</p>
<p>This work was performed for QBayLogic as part of their <a href="https://well-typed.com/ecosystem">Haskell Ecosystem Support Package</a>.
If your company is using Haskell and from time to time requires expert help in
issues like this, our packages fund
maintenance on core tooling such as GHC and Cabal, as well as development or support for your specific issues.
Please contact <a href="mailto:info@well-typed.com">info@well-typed.com</a> if we
might be able to help you!</p>
]]></content:encoded>
</item>
<item>
    <title>Verifying and testing timeliness constraints with io-sim</title>
    <link>https://well-typed.com/blog/2025/10/an-introduction-to-io-sim</link>
    <description><![CDATA[Testing and verifying concurrent systems is hard due to their
non-deterministic nature --- verifying behavior that changes with each execution
is difficult. Race conditions thrive in the non-deterministic world of thread
scheduling. Even more challenging is verifying timeliness constraints, i.e.
ensuring [...]]]></description>
    <dc:creator>armando</dc:creator>
    <category>well-typed</category>
    <category>io-sim</category>
    <category>simulation</category>
    <category>testing</category>
    <guid isPermaLink="false">http://www.well-typed.com/blog/2025/10/an-introduction-to-io-sim</guid>
    <pubDate>Mon, 13 Oct 2025 00:00:00 GMT</pubDate>
    <content:encoded><![CDATA[<p>Testing and verifying concurrent systems is hard due to their
non-deterministic nature — verifying behavior that changes with each execution
is difficult. Race conditions thrive in the non-deterministic world of thread
scheduling. Even more challenging is verifying timeliness constraints, i.e.
ensuring that operations complete within specified deadlines or that service
guarantees are maintained under load. Traditional testing approaches struggle
with concurrency, and mocking strategies often fail to capture the subtle
interactions between threads, time, and shared state that cause real
production failures.</p>
<p>The <a href="https://hackage.haskell.org/package/io-sim"><code>io-sim</code></a> Haskell library, developed
by Well-Typed in partnership with engineers from IOG and Quviq, offers a
compelling solution to this problem. The library provides a pure simulation environment
for IO computations, enabling deterministic execution of concurrent code with
accurate time simulation and detailed execution traces. Unlike other testing
approaches, with <code>io-sim</code> one is able to write highly concurrent, real-time
systems and verify their timeliness constraints in a deterministic manner, by
accurately simulating GHC’s runtime system (e.g. asynchronous exceptions,
timeouts &amp; delays, etc.).</p>
<p>This blog post introduces and explores <code>io-sim</code> through a practical example:
debugging an elevator controller that violates its response time requirements.</p>
<!-- more -->
<blockquote>
<p>There’s also
<a href="https://engineering.iog.io/2023-04-14-io-sim-annoucement/">this great blog post</a>
announcing <code>io-sim</code> and it goes a bit more into detail about its features!</p>
</blockquote>
<h3 id="the-problem">The Problem</h3>
<p>Consider a simple elevator located in a three-floor building (ground, first
, second). It takes roughly 1 second for the elevator to go up and
down between each floor. The service requirement is:
<strong>no passenger should wait more than 4 seconds</strong> from pressing the
call button until the elevator doors open at their floor. It should be possible
to test and verify this requirement when writing our elevator controller.</p>
<p>This ensures a reasonable quality of service and prevents frustration. Given
the short distance between floors, 4 seconds is sensible. In the worst case,
the elevator must travel from ground to second floor and back again.</p>
<p>Here’s a first attempt at modelling the system. Let’s start with the core data
structures:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">Direction</span> <span class="ot">=</span> <span class="dt">Up</span> <span class="op">|</span> <span class="dt">Down</span> <span class="op">|</span> <span class="dt">None</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a>  <span class="kw">deriving</span> (<span class="dt">Eq</span>, <span class="dt">Show</span>)</span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">Floor</span> <span class="ot">=</span> <span class="dt">Ground</span> <span class="op">|</span> <span class="dt">First</span> <span class="op">|</span> <span class="dt">Second</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a>  <span class="kw">deriving</span> (<span class="dt">Eq</span>, <span class="dt">Ord</span>, <span class="dt">Show</span>, <span class="dt">Enum</span>)</span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">ElevatorState</span> <span class="ot">=</span> <span class="dt">ElevatorState</span></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a>  {<span class="ot"> currentFloor ::</span> <span class="dt">Floor</span></span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a>  ,<span class="ot"> moving       ::</span> <span class="dt">Direction</span></span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a>  ,<span class="ot"> requests     ::</span> [<span class="dt">Floor</span>]</span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a>  } <span class="kw">deriving</span> (<span class="dt">Show</span>)</span></code></pre></div>
<p>The elevator’s state tracks three things: where it currently is, which
direction it’s moving (if any), and a queue of floor requests.</p>
<p>The system has two main components that run concurrently:</p>
<ul>
<li>An elevator controller that continuously processes the request queue</li>
<li>Button press handler that adds new floor requests</li>
</ul>
<p>Let’s look at the controller first:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- | Initialize an empty elevator state.</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="co">--</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a><span class="co">-- The elevator starts on the ground floor</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a><span class="co">--</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a><span class="ot">initElevator ::</span> <span class="dt">IO</span> (<span class="dt">TVar</span> <span class="dt">ElevatorState</span>)</span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a>initElevator <span class="ot">=</span> newTVarIO <span class="op">$</span> <span class="dt">ElevatorState</span> <span class="dt">Ground</span> <span class="dt">None</span> []</span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a><span class="co">-- | Elevator controller logic.</span></span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a><span class="co">--</span></span>
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a><span class="co">-- 1. Read the current 'ElevatorState'</span></span>
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a><span class="co">-- 2. Check if there are any requested floors</span></span>
<span id="cb2-12"><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a><span class="co">-- 3.</span></span>
<span id="cb2-13"><a href="#cb2-13" aria-hidden="true" tabindex="-1"></a><span class="co">--    3.1. Block waiting for new requests if there aren't any</span></span>
<span id="cb2-14"><a href="#cb2-14" aria-hidden="true" tabindex="-1"></a><span class="co">--    3.2. If there any requests, move to the floor at the top of the queue.</span></span>
<span id="cb2-15"><a href="#cb2-15" aria-hidden="true" tabindex="-1"></a><span class="co">--</span></span>
<span id="cb2-16"><a href="#cb2-16" aria-hidden="true" tabindex="-1"></a><span class="co">-- Straightforward FIFO elevator algorithm.</span></span>
<span id="cb2-17"><a href="#cb2-17" aria-hidden="true" tabindex="-1"></a><span class="co">--</span></span>
<span id="cb2-18"><a href="#cb2-18" aria-hidden="true" tabindex="-1"></a><span class="ot">elevatorController ::</span> <span class="dt">TVar</span> <span class="dt">ElevatorState</span> <span class="ot">-&gt;</span> <span class="dt">IO</span> ()</span>
<span id="cb2-19"><a href="#cb2-19" aria-hidden="true" tabindex="-1"></a>elevatorController elevatorVar <span class="ot">=</span> forever <span class="op">$</span> <span class="kw">do</span></span>
<span id="cb2-20"><a href="#cb2-20" aria-hidden="true" tabindex="-1"></a>  <span class="co">-- Atomically get the next floor from the queue</span></span>
<span id="cb2-21"><a href="#cb2-21" aria-hidden="true" tabindex="-1"></a>  (nextFloor, dir) <span class="ot">&lt;-</span> atomically <span class="op">$</span> <span class="kw">do</span></span>
<span id="cb2-22"><a href="#cb2-22" aria-hidden="true" tabindex="-1"></a>    state <span class="ot">&lt;-</span> readTVar elevatorVar</span>
<span id="cb2-23"><a href="#cb2-23" aria-hidden="true" tabindex="-1"></a>    <span class="kw">case</span> requests state <span class="kw">of</span></span>
<span id="cb2-24"><a href="#cb2-24" aria-hidden="true" tabindex="-1"></a>      []               <span class="ot">-&gt;</span> retry  <span class="co">-- Block until a request arrives</span></span>
<span id="cb2-25"><a href="#cb2-25" aria-hidden="true" tabindex="-1"></a>      (targetFloor<span class="op">:</span>rs) <span class="ot">-&gt;</span> <span class="kw">do</span></span>
<span id="cb2-26"><a href="#cb2-26" aria-hidden="true" tabindex="-1"></a>        <span class="co">-- Remove the floor from queue and start moving</span></span>
<span id="cb2-27"><a href="#cb2-27" aria-hidden="true" tabindex="-1"></a>        <span class="kw">let</span> direction <span class="ot">=</span> getDirection (currentFloor state) targetFloor</span>
<span id="cb2-28"><a href="#cb2-28" aria-hidden="true" tabindex="-1"></a>        writeTVar elevatorVar <span class="op">$</span> state</span>
<span id="cb2-29"><a href="#cb2-29" aria-hidden="true" tabindex="-1"></a>          { moving <span class="ot">=</span> direction, requests <span class="ot">=</span> rs }</span>
<span id="cb2-30"><a href="#cb2-30" aria-hidden="true" tabindex="-1"></a>        <span class="fu">return</span> (targetFloor, direction)</span>
<span id="cb2-31"><a href="#cb2-31" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-32"><a href="#cb2-32" aria-hidden="true" tabindex="-1"></a>  <span class="fu">putStrLn</span> (<span class="st">&quot;Going &quot;</span> <span class="op">++</span> <span class="fu">show</span> dir <span class="op">++</span> <span class="st">&quot; to &quot;</span> <span class="op">++</span> <span class="fu">show</span> nextFloor)</span>
<span id="cb2-33"><a href="#cb2-33" aria-hidden="true" tabindex="-1"></a>  moveToFloor elevatorVar nextFloor</span></code></pre></div>
<p>The <code>moveToFloor</code> function simulates the physical movement of the elevator.</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="ot">moveToFloor ::</span> <span class="dt">TVar</span> <span class="dt">ElevatorState</span> <span class="ot">-&gt;</span> <span class="dt">Floor</span> <span class="ot">-&gt;</span> <span class="dt">IO</span> ()</span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a>moveToFloor elevatorVar targetFloor <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a>  elevatorState <span class="ot">&lt;-</span> readTVarIO elevatorVar</span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a>  when (currentFloor elevatorState <span class="op">/=</span> targetFloor) <span class="op">$</span> <span class="kw">do</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a>    <span class="co">-- Takes 1 second to move between floors</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a>    threadDelay (<span class="dv">1000000</span> <span class="op">*</span> numberOfFloorsToMove)</span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a>    atomically <span class="op">$</span></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a>      modifyTVar elevatorVar (\elevatorState' <span class="ot">-&gt;</span></span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a>        elevatorState' { currentFloor <span class="ot">=</span> targetFloor</span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a>                       , moving       <span class="ot">=</span> <span class="dt">Idle</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>
<span id="cb3-13"><a href="#cb3-13" aria-hidden="true" tabindex="-1"></a>  <span class="fu">putStrLn</span> (<span class="st">&quot;Arrived at &quot;</span> <span class="op">++</span> <span class="fu">show</span> targetFloor)</span></code></pre></div>
<p>The <code>buttonPress</code> function handles both external calls (someone waiting for
the elevator) and internal requests (someone inside selecting a destination):</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- | Whenever a button is pressed this function is called.</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="co">--</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a><span class="co">-- There are two scenarios when a button is pressed:</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a><span class="co">--</span></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a><span class="co">-- 1. When a person is calling the elevator to a floor in order to enter it.</span></span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a><span class="co">-- 2. When a person is inside the elevator and wants to instruct the elevator</span></span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a><span class="co">--    to go to a particular floor.</span></span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a><span class="co">--</span></span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a><span class="ot">buttonPress ::</span> <span class="dt">TVar</span> <span class="dt">ElevatorState</span> <span class="ot">-&gt;</span> <span class="dt">Floor</span> <span class="ot">-&gt;</span> <span class="dt">IO</span> ()</span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a>buttonPress elevatorVar <span class="fu">floor</span> <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb4-11"><a href="#cb4-11" aria-hidden="true" tabindex="-1"></a>  <span class="fu">putStrLn</span> (<span class="st">&quot;Pressing button to &quot;</span> <span class="op">++</span> <span class="fu">show</span> <span class="fu">floor</span>)</span>
<span id="cb4-12"><a href="#cb4-12" aria-hidden="true" tabindex="-1"></a>  atomically <span class="op">$</span></span>
<span id="cb4-13"><a href="#cb4-13" aria-hidden="true" tabindex="-1"></a>    modifyTVar elevatorVar <span class="op">$</span> \state <span class="ot">-&gt;</span> <span class="kw">do</span></span>
<span id="cb4-14"><a href="#cb4-14" aria-hidden="true" tabindex="-1"></a>      <span class="kw">case</span> requests state <span class="kw">of</span></span>
<span id="cb4-15"><a href="#cb4-15" aria-hidden="true" tabindex="-1"></a>        rs<span class="op">@</span>(nextFloor<span class="op">:</span>_)</span>
<span id="cb4-16"><a href="#cb4-16" aria-hidden="true" tabindex="-1"></a>          <span class="op">|</span> <span class="kw">let</span> mostRecentRequestedFloor <span class="ot">=</span> <span class="fu">last</span> rs</span>
<span id="cb4-17"><a href="#cb4-17" aria-hidden="true" tabindex="-1"></a>          ,    nextFloor <span class="op">/=</span> <span class="fu">floor</span></span>
<span id="cb4-18"><a href="#cb4-18" aria-hidden="true" tabindex="-1"></a>            <span class="op">||</span> mostRecentRequestedFloor <span class="op">/=</span> <span class="fu">floor</span> <span class="ot">-&gt;</span></span>
<span id="cb4-19"><a href="#cb4-19" aria-hidden="true" tabindex="-1"></a>            state { requests <span class="ot">=</span> rs <span class="op">++</span> [<span class="fu">floor</span>] }</span>
<span id="cb4-20"><a href="#cb4-20" aria-hidden="true" tabindex="-1"></a>          <span class="op">|</span> <span class="fu">otherwise</span> <span class="ot">-&gt;</span> state</span>
<span id="cb4-21"><a href="#cb4-21" aria-hidden="true" tabindex="-1"></a>        [] <span class="ot">-&gt;</span> state { requests <span class="ot">=</span> [<span class="fu">floor</span>] }</span></code></pre></div>
<p>Consider the following example scenario and timeline:</p>
<ol start="0" type="1">
<li>The elevator starts on the ground floor.</li>
<li>Person A is on the first floor and presses the button to call the elevator
to the first floor.</li>
<li>While the elevator is going up, Person B arrives on the ground floor calls
it to the ground floor.</li>
<li>Elevator arrives at the first floor.</li>
<li>Person A enters and presses the button to go to the second floor.</li>
<li>Elevator goes to the ground floor to pick up Person B.</li>
<li>Person B enters and presses the button to go to the first floor.</li>
<li>Elevator goes to the second floor.</li>
<li>Elevator goes to the first floor.</li>
</ol>
<div class="sourceCode" id="cb5"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- | This example mimicks the scenario above, pressing buttons in the right</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a><span class="co">-- order.</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a><span class="ot">elevatorExample ::</span> [<span class="dt">Floor</span>] <span class="ot">-&gt;</span> <span class="dt">IO</span> ()</span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a>elevatorExample floors <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a>  elevator <span class="ot">&lt;-</span> initElevator</span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a>  withAsync (elevatorController elevator)</span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a>    <span class="op">$</span> \controllerAsync <span class="ot">-&gt;</span> <span class="kw">do</span></span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a>        <span class="co">-- Simulate multiple people pressing buttons simultaneously</span></span>
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a>        forConcurrently_ floors (buttonPress elevator)</span>
<span id="cb5-10"><a href="#cb5-10" aria-hidden="true" tabindex="-1"></a>        threadDelay (<span class="dv">10</span> <span class="op">*</span> <span class="dv">1000000</span>)</span>
<span id="cb5-11"><a href="#cb5-11" aria-hidden="true" tabindex="-1"></a>        cancel controllerAsync</span>
<span id="cb5-12"><a href="#cb5-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-13"><a href="#cb5-13" aria-hidden="true" tabindex="-1"></a>elevatorExample [<span class="dt">First</span>, <span class="dt">Ground</span>, <span class="dt">Second</span>, <span class="dt">First</span>]</span></code></pre></div>
<p>This function spawns the elevator controller and then simulates multiple
button presses happening concurrently. Let’s trace through our example:</p>
<pre><code>Pressing button to First
Going Up to First
Pressing button to Ground
Pressing button to Second
Pressing button to First
Arrived at First
Going Down to Ground
Arrived at Ground
Going Up to Second
Arrived at Second
Going Down to First
Arrived at First</code></pre>
<p>Does such a simple implementation adhere to the specified time constraints?
The answer is no, a FIFO elevator algorithm is easy to implement but can be
inefficient if the requests are spread out across floors, leading to more
travel time.</p>
<p>How would one go about to test/verify this? Testing timeliness
constraints in concurrent IO is tricky, due to its non-deterministic nature
and limited observability.</p>
<h3 id="io-sim-deterministic-io-simulator"><code>io-sim</code>: Deterministic IO Simulator</h3>
<p><code>io-sim</code> closes the gap between the code that’s actually run in
production and the code that runs in tests. Combined with property based
testing techniques it is possible to simulate execution of a program for years
worth of simulated time and find reproducible, rare edge-case bugs.</p>
<p><code>io-sim</code> achieves this by taking advantage of the
<a href="https://hackage.haskell.org/package/io-classes"><code>io-classes</code></a> set of packages,
which offers a class-based API compatible with most of the core Haskell
packages, including <code>mtl</code>. In general the APIs follow the <code>base</code> or <code>async</code></p>
<p><code>io-sim</code> is a time based, discrete event simulator. Which means, it provides a
granular execution trace that can be used from inspecting the commit order of
STM transactions to validating a high level, temporal logic property over some
abstract trace. The best part is that code requires minimal changes to use
<code>io-sim</code>, just polymorphic type signatures that work with both <code>IO</code> and
<code>IOSim</code> monads. Here’s the elevator controller code refactored for testing
with <code>io-sim</code>:</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="ot">initElevator ::</span> <span class="dt">MonadSTM</span> m <span class="ot">=&gt;</span> m (<span class="dt">TVar</span> m <span class="dt">ElevatorState</span>)</span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a>initElevator <span class="ot">=</span> <span class="op">...</span></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a>elevatorController</span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a><span class="ot">  ::</span> ( <span class="dt">MonadSTM</span> m</span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a>     , <span class="dt">MonadDelay</span> m</span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a>     , <span class="dt">MonadSay</span> m</span>
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true" tabindex="-1"></a>     )</span>
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true" tabindex="-1"></a>  <span class="ot">=&gt;</span> <span class="dt">TVar</span> m <span class="dt">ElevatorState</span> <span class="ot">-&gt;</span> m ()</span>
<span id="cb7-10"><a href="#cb7-10" aria-hidden="true" tabindex="-1"></a>elevatorController elevatorVar <span class="ot">=</span></span>
<span id="cb7-11"><a href="#cb7-11" aria-hidden="true" tabindex="-1"></a>  <span class="op">...</span></span>
<span id="cb7-12"><a href="#cb7-12" aria-hidden="true" tabindex="-1"></a>  say (<span class="st">&quot;Going &quot;</span> <span class="op">++</span> <span class="fu">show</span> dir <span class="op">++</span> <span class="st">&quot; to &quot;</span> <span class="op">++</span> <span class="fu">show</span> nextFloor)</span>
<span id="cb7-13"><a href="#cb7-13" aria-hidden="true" tabindex="-1"></a>  <span class="op">...</span></span>
<span id="cb7-14"><a href="#cb7-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-15"><a href="#cb7-15" aria-hidden="true" tabindex="-1"></a>moveToFloor</span>
<span id="cb7-16"><a href="#cb7-16" aria-hidden="true" tabindex="-1"></a><span class="ot">  ::</span> ( <span class="dt">MonadSTM</span> m</span>
<span id="cb7-17"><a href="#cb7-17" aria-hidden="true" tabindex="-1"></a>     , <span class="dt">MonadDelay</span> m</span>
<span id="cb7-18"><a href="#cb7-18" aria-hidden="true" tabindex="-1"></a>     , <span class="dt">MonadSay</span> m</span>
<span id="cb7-19"><a href="#cb7-19" aria-hidden="true" tabindex="-1"></a>     )</span>
<span id="cb7-20"><a href="#cb7-20" aria-hidden="true" tabindex="-1"></a>  <span class="ot">=&gt;</span> <span class="dt">TVar</span> m <span class="dt">ElevatorState</span> <span class="ot">-&gt;</span> <span class="dt">Floor</span> <span class="ot">-&gt;</span> m ()</span>
<span id="cb7-21"><a href="#cb7-21" aria-hidden="true" tabindex="-1"></a>moveToFloor elevatorVar targetFloor <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb7-22"><a href="#cb7-22" aria-hidden="true" tabindex="-1"></a>  <span class="op">...</span></span>
<span id="cb7-23"><a href="#cb7-23" aria-hidden="true" tabindex="-1"></a>  say (<span class="st">&quot;Arrived at &quot;</span> <span class="op">++</span> <span class="fu">show</span> targetFloor)</span>
<span id="cb7-24"><a href="#cb7-24" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-25"><a href="#cb7-25" aria-hidden="true" tabindex="-1"></a><span class="ot">getDirection ::</span> <span class="dt">Floor</span> <span class="ot">-&gt;</span> <span class="dt">Floor</span> <span class="ot">-&gt;</span> <span class="dt">Direction</span></span>
<span id="cb7-26"><a href="#cb7-26" aria-hidden="true" tabindex="-1"></a>getDirection from to <span class="ot">=</span> <span class="op">...</span></span>
<span id="cb7-27"><a href="#cb7-27" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-28"><a href="#cb7-28" aria-hidden="true" tabindex="-1"></a>buttonPress</span>
<span id="cb7-29"><a href="#cb7-29" aria-hidden="true" tabindex="-1"></a><span class="ot">  ::</span> ( <span class="dt">MonadSTM</span> m</span>
<span id="cb7-30"><a href="#cb7-30" aria-hidden="true" tabindex="-1"></a>     , <span class="dt">MonadSay</span> m</span>
<span id="cb7-31"><a href="#cb7-31" aria-hidden="true" tabindex="-1"></a>     )</span>
<span id="cb7-32"><a href="#cb7-32" aria-hidden="true" tabindex="-1"></a>  <span class="ot">=&gt;</span> <span class="dt">TVar</span> m <span class="dt">ElevatorState</span> <span class="ot">-&gt;</span> <span class="dt">Floor</span> <span class="ot">-&gt;</span> m ()</span>
<span id="cb7-33"><a href="#cb7-33" aria-hidden="true" tabindex="-1"></a>buttonPress elevatorVar <span class="fu">floor</span> <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb7-34"><a href="#cb7-34" aria-hidden="true" tabindex="-1"></a>  say (<span class="st">&quot;Pressing button to &quot;</span> <span class="op">++</span> <span class="fu">show</span> <span class="fu">floor</span>)</span>
<span id="cb7-35"><a href="#cb7-35" aria-hidden="true" tabindex="-1"></a>  <span class="op">...</span></span>
<span id="cb7-36"><a href="#cb7-36" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-37"><a href="#cb7-37" aria-hidden="true" tabindex="-1"></a>elevatorExample</span>
<span id="cb7-38"><a href="#cb7-38" aria-hidden="true" tabindex="-1"></a><span class="ot">  ::</span> ( <span class="dt">MonadSTM</span> m</span>
<span id="cb7-39"><a href="#cb7-39" aria-hidden="true" tabindex="-1"></a>     , <span class="dt">MonadAsync</span> m</span>
<span id="cb7-40"><a href="#cb7-40" aria-hidden="true" tabindex="-1"></a>     , <span class="dt">MonadDelay</span> m</span>
<span id="cb7-41"><a href="#cb7-41" aria-hidden="true" tabindex="-1"></a>     , <span class="dt">MonadSay</span> m</span>
<span id="cb7-42"><a href="#cb7-42" aria-hidden="true" tabindex="-1"></a>     )</span>
<span id="cb7-43"><a href="#cb7-43" aria-hidden="true" tabindex="-1"></a>  <span class="ot">=&gt;</span> [<span class="dt">Floor</span>]</span>
<span id="cb7-44"><a href="#cb7-44" aria-hidden="true" tabindex="-1"></a>  <span class="ot">-&gt;</span> m ()</span>
<span id="cb7-45"><a href="#cb7-45" aria-hidden="true" tabindex="-1"></a>elevatorExample floors <span class="ot">=</span> <span class="op">...</span></span></code></pre></div>
<p>Notice that only type signatures and <code>IO</code> operations needed changes. The core
business logic remains identical. When instantiated to IO, <code>say</code> becomes
<code>putStrLn</code>, but in the <code>IOSim</code> monad it produces traceable events.</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="ot">main ::</span> <span class="dt">IO</span> ()</span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a>main <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a>  <span class="kw">let</span><span class="ot"> simpleExample ::</span> [<span class="dt">Floor</span>]</span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a>      simpleExample <span class="ot">=</span> [<span class="dt">First</span>, <span class="dt">Ground</span>, <span class="dt">Second</span>, <span class="dt">First</span>]</span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-6"><a href="#cb8-6" aria-hidden="true" tabindex="-1"></a>  <span class="co">-- Runs the 'elevatorExample' in IO. This outputs exactly the same output</span></span>
<span id="cb8-7"><a href="#cb8-7" aria-hidden="true" tabindex="-1"></a>  <span class="co">-- as before</span></span>
<span id="cb8-8"><a href="#cb8-8" aria-hidden="true" tabindex="-1"></a>  elevatorExample simpleExample</span>
<span id="cb8-9"><a href="#cb8-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-10"><a href="#cb8-10" aria-hidden="true" tabindex="-1"></a>  <span class="co">-- Runs the 'elevatorExample' in IOSim.</span></span>
<span id="cb8-11"><a href="#cb8-11" aria-hidden="true" tabindex="-1"></a>  <span class="fu">putStrLn</span> <span class="op">.</span> intercalate <span class="st">&quot;\n&quot;</span></span>
<span id="cb8-12"><a href="#cb8-12" aria-hidden="true" tabindex="-1"></a>           <span class="op">.</span> <span class="fu">map</span> <span class="fu">show</span></span>
<span id="cb8-13"><a href="#cb8-13" aria-hidden="true" tabindex="-1"></a>           <span class="op">.</span> selectTraceEventsSayWithTime</span>
<span id="cb8-14"><a href="#cb8-14" aria-hidden="true" tabindex="-1"></a>           <span class="co">-- ^ Extracts only the 'say' events from the 'SimTrace' and</span></span>
<span id="cb8-15"><a href="#cb8-15" aria-hidden="true" tabindex="-1"></a>           <span class="co">-- attaches the timestamp for each event</span></span>
<span id="cb8-16"><a href="#cb8-16" aria-hidden="true" tabindex="-1"></a>           <span class="co">--</span></span>
<span id="cb8-17"><a href="#cb8-17" aria-hidden="true" tabindex="-1"></a>           <span class="co">-- selectTraceEventsSayWithTime :: SimTrace a -&gt; [(Time, String)]</span></span>
<span id="cb8-18"><a href="#cb8-18" aria-hidden="true" tabindex="-1"></a>           <span class="co">--</span></span>
<span id="cb8-19"><a href="#cb8-19" aria-hidden="true" tabindex="-1"></a>           <span class="co">-- This function takes a 'SimTrace' and filters all 'EventSay'</span></span>
<span id="cb8-20"><a href="#cb8-20" aria-hidden="true" tabindex="-1"></a>           <span class="co">-- traces. It also captures the time of the trace event.</span></span>
<span id="cb8-21"><a href="#cb8-21" aria-hidden="true" tabindex="-1"></a>           <span class="op">$</span> runSimTrace (elevatorExample simpleExample)</span>
<span id="cb8-22"><a href="#cb8-22" aria-hidden="true" tabindex="-1"></a>           <span class="co">-- ^ Runs example in 'IOSim'</span></span>
<span id="cb8-23"><a href="#cb8-23" aria-hidden="true" tabindex="-1"></a>           <span class="co">--</span></span>
<span id="cb8-24"><a href="#cb8-24" aria-hidden="true" tabindex="-1"></a>           <span class="co">-- runSimTrace :: (forall s. IOSim s a) -&gt; SimTrace a</span></span>
<span id="cb8-25"><a href="#cb8-25" aria-hidden="true" tabindex="-1"></a>           <span class="co">--</span></span>
<span id="cb8-26"><a href="#cb8-26" aria-hidden="true" tabindex="-1"></a>           <span class="co">-- This function runs a IOSim program, yielding an execution trace.</span></span></code></pre></div>
<p>Running the program above, the first noticeable thing is that when the program
runs in <code>IO</code>, it actually takes 10 real seconds to run due to the
<code>threadDelay</code> calls. However, when the program runs in <code>IOSim</code> the output is
instantaneous. This is because <code>io-sim</code> operates on simulated time rather than
wall-clock time, i.e. only the internal clock advances when threads execute
time-dependent operations like <code>threadDelay</code> or <code>timeout</code>s. Between these
operations, the simulation executes as if it had infinite CPU speed, i.e. all
computations at a given timestamp complete instantly, yet remain sequentially
ordered and deterministic.</p>
<pre><code>(Time 0s,&quot;Pressing button to First&quot;)
(Time 0s,&quot;Going Up to First&quot;)
(Time 0s,&quot;Pressing button to Ground&quot;)
(Time 0s,&quot;Pressing button to Second&quot;)
(Time 0s,&quot;Pressing button to First&quot;)
(Time 1s,&quot;Arrived at First&quot;)
(Time 1s,&quot;Going Down to Ground&quot;)
(Time 2s,&quot;Arrived at Ground&quot;)
(Time 2s,&quot;Going Up to Second&quot;)
(Time 4s,&quot;Arrived at Second&quot;)
(Time 4s,&quot;Going Down to First&quot;)
(Time 5s,&quot;Arrived at First&quot;)</code></pre>
<p>This particular scenario doesn’t violate the constraint. To find violations,
property-based testing can explore the space of possible request patterns. The
only problem is that our <code>say</code> traces are strings which is not a very
<em>functional</em> way of tracing things.</p>
<h3 id="contra-tracer-structured-tracing">contra-tracer: Structured Tracing</h3>
<p>While <code>say</code> provides basic, string-based tracing, real systems need structured
tracing of domain-specific events. String-based logging quickly becomes
inadequate when trying to verify complex properties or analyze system behavior
programmatically. Tracing strongly-typed events that can be filtered,
analyzed, and used in property tests is much better. The
<a href="https://hackage.haskell.org/package/contra-tracer"><code>contra-tracer</code></a> library
provides a contravariant tracing abstraction that integrates seamlessly with
<code>io-sim</code>.</p>
<p>The key advantages of structured tracing:</p>
<ul>
<li>Type Safety: Events are strongly typed, preventing typos and logging errors.</li>
<li>Composability: Tracers can be filtered, transformed, and combined.</li>
<li>Testability: Events can be programmatically analyzed in tests.</li>
</ul>
<p>All one needs to do is to have a custom trace type:</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">ElevatorTrace</span> <span class="ot">=</span> <span class="dt">ButtonPress</span> <span class="dt">Floor</span></span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a>                   <span class="op">|</span> <span class="dt">Going</span> <span class="dt">Direction</span> <span class="dt">Floor</span></span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a>                   <span class="op">|</span> <span class="dt">ArrivedAt</span> <span class="dt">Floor</span></span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a>                   <span class="kw">deriving</span> (<span class="dt">Eq</span>, <span class="dt">Show</span>, <span class="dt">Typeable</span>)</span></code></pre></div>
<p>And substitute all calls to <code>say</code> for <code>traceWith tracer (ButtonPress floor)</code>,
for example.</p>
<p>With structured tracing in place, extracting and analyzing traces becomes
type-safe and straightforward:</p>
<div class="sourceCode" id="cb11"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- | Extract typed elevator events with timestamps</span></span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a><span class="ot">extractElevatorEvents ::</span> <span class="dt">SimTrace</span> a <span class="ot">-&gt;</span> [(<span class="dt">Time</span>, <span class="dt">ElevatorTrace</span>)]</span>
<span id="cb11-4"><a href="#cb11-4" aria-hidden="true" tabindex="-1"></a>extractElevatorEvents <span class="ot">=</span></span>
<span id="cb11-5"><a href="#cb11-5" aria-hidden="true" tabindex="-1"></a>  selectTraceEventsDynamicWithTime</span></code></pre></div>
<h3 id="property-based-testing-verifying-timing-constraints">Property-Based Testing: Verifying Timing Constraints</h3>
<p>The elevator system began with a clear requirement: no passenger should wait
more than 4 seconds. The FIFO implementation seemed reasonable, but the elevator
can end up travelling between the bottom and top floors whilst someone in the middle waits
their turn.</p>
<p>With typed traces from <code>contra-tracer</code> and deterministic simulation from
<code>io-sim</code>, <code>QuickCheck</code> can systematically explore the space of possible request
patterns and verify this property.</p>
<p>To verify our timing constraint, we need to:</p>
<ol type="1">
<li>Generate random sequences of floor requests</li>
<li>Run each sequence through the elevator simulation</li>
<li>Check that every passenger gets service within 4 seconds</li>
</ol>
<p>Let’s start with the test data generation:</p>
<div class="sourceCode" id="cb12"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- | 'Floor' Arbitrary instance.</span></span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a><span class="co">--</span></span>
<span id="cb12-3"><a href="#cb12-3" aria-hidden="true" tabindex="-1"></a><span class="co">-- Randomly generate floors. The shrink instance is the most important here</span></span>
<span id="cb12-4"><a href="#cb12-4" aria-hidden="true" tabindex="-1"></a><span class="co">-- since it will be responsible for generating a simpler counterexample.</span></span>
<span id="cb12-5"><a href="#cb12-5" aria-hidden="true" tabindex="-1"></a><span class="co">--</span></span>
<span id="cb12-6"><a href="#cb12-6" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">Arbitrary</span> <span class="dt">Floor</span> <span class="kw">where</span></span>
<span id="cb12-7"><a href="#cb12-7" aria-hidden="true" tabindex="-1"></a>  arbitrary <span class="ot">=</span> elements [<span class="dt">Ground</span>, <span class="dt">First</span>, <span class="dt">Second</span>]</span>
<span id="cb12-8"><a href="#cb12-8" aria-hidden="true" tabindex="-1"></a>  shrink <span class="dt">Second</span> <span class="ot">=</span> [<span class="dt">Ground</span>, <span class="dt">First</span>]</span>
<span id="cb12-9"><a href="#cb12-9" aria-hidden="true" tabindex="-1"></a>  shrink <span class="dt">First</span>  <span class="ot">=</span> [<span class="dt">Ground</span>]</span>
<span id="cb12-10"><a href="#cb12-10" aria-hidden="true" tabindex="-1"></a>  shrink <span class="dt">Ground</span>   <span class="ot">=</span> []</span></code></pre></div>
<p>The shrinking strategy is important because when <code>QuickCheck</code> finds a failing
case with many floors, it will try simpler combinations to find the minimal
reproduction of the original input.</p>
<p>To verify the property that no passenger waits more than 4 seconds for the
elevator to arrive to its floor, one needs to track the button presses and
measure how long until the elevator arrives.</p>
<p>The property works by maintaining a map of pending requests. Each
<code>ButtonPress</code> adds an entry (keeping the earliest if multiple people request
the same floor), and each <code>ArrivedAt</code> checks if that floor was requested and
whether the wait exceeded 4 seconds:</p>
<div class="sourceCode" id="cb13"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- Traverse the event trace and check if there is any gap longer than 4s</span></span>
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a><span class="co">-- between requests and the elevator arriving at the request's floor.</span></span>
<span id="cb13-3"><a href="#cb13-3" aria-hidden="true" tabindex="-1"></a><span class="co">--</span></span>
<span id="cb13-4"><a href="#cb13-4" aria-hidden="true" tabindex="-1"></a><span class="ot">violatesFourSecondRule ::</span> [(<span class="dt">Time</span>, <span class="dt">ElevatorTrace</span>)] <span class="ot">-&gt;</span> <span class="dt">Property</span></span>
<span id="cb13-5"><a href="#cb13-5" aria-hidden="true" tabindex="-1"></a>violatesFourSecondRule events <span class="ot">=</span> counterexample (intercalate <span class="st">&quot;\n&quot;</span> <span class="op">$</span> <span class="fu">map</span> <span class="fu">show</span> events)</span>
<span id="cb13-6"><a href="#cb13-6" aria-hidden="true" tabindex="-1"></a>                              <span class="op">$</span> checkViolations events Map.empty</span>
<span id="cb13-7"><a href="#cb13-7" aria-hidden="true" tabindex="-1"></a>  <span class="kw">where</span></span>
<span id="cb13-8"><a href="#cb13-8" aria-hidden="true" tabindex="-1"></a><span class="ot">    checkViolations ::</span> [(<span class="dt">Time</span>, <span class="dt">ElevatorTrace</span>)] <span class="ot">-&gt;</span> <span class="dt">Map</span> <span class="dt">Floor</span> <span class="dt">DiffTime</span> <span class="ot">-&gt;</span> <span class="dt">Property</span></span>
<span id="cb13-9"><a href="#cb13-9" aria-hidden="true" tabindex="-1"></a>    <span class="co">-- Fail if there are pending requests</span></span>
<span id="cb13-10"><a href="#cb13-10" aria-hidden="true" tabindex="-1"></a>    checkViolations [] pending <span class="ot">=</span></span>
<span id="cb13-11"><a href="#cb13-11" aria-hidden="true" tabindex="-1"></a>      counterexample (<span class="st">&quot;Elevator never arrived at: &quot;</span> <span class="op">++</span> <span class="fu">show</span> pending)</span>
<span id="cb13-12"><a href="#cb13-12" aria-hidden="true" tabindex="-1"></a>                     (Map.null pending)</span>
<span id="cb13-13"><a href="#cb13-13" aria-hidden="true" tabindex="-1"></a>    checkViolations ((<span class="dt">Time</span> t, event)<span class="op">:</span>rest) pending <span class="ot">=</span></span>
<span id="cb13-14"><a href="#cb13-14" aria-hidden="true" tabindex="-1"></a>      <span class="kw">case</span> event <span class="kw">of</span></span>
<span id="cb13-15"><a href="#cb13-15" aria-hidden="true" tabindex="-1"></a>        <span class="co">-- Add request to the pending requests map. Note that if there's</span></span>
<span id="cb13-16"><a href="#cb13-16" aria-hidden="true" tabindex="-1"></a>        <span class="co">-- already a request for a particular floor, overwriting the</span></span>
<span id="cb13-17"><a href="#cb13-17" aria-hidden="true" tabindex="-1"></a>        <span class="co">-- timestamp is not the right thing to do because there's an older</span></span>
<span id="cb13-18"><a href="#cb13-18" aria-hidden="true" tabindex="-1"></a>        <span class="co">-- request that shouldn't be forgotten.</span></span>
<span id="cb13-19"><a href="#cb13-19" aria-hidden="true" tabindex="-1"></a>        <span class="co">--</span></span>
<span id="cb13-20"><a href="#cb13-20" aria-hidden="true" tabindex="-1"></a>        <span class="dt">ButtonPress</span> <span class="fu">floor</span> <span class="ot">-&gt;</span></span>
<span id="cb13-21"><a href="#cb13-21" aria-hidden="true" tabindex="-1"></a>          checkViolations rest (Map.alter (<span class="fu">maybe</span> (<span class="dt">Just</span> t) <span class="dt">Just</span>) <span class="fu">floor</span> pending)</span>
<span id="cb13-22"><a href="#cb13-22" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb13-23"><a href="#cb13-23" aria-hidden="true" tabindex="-1"></a>        <span class="co">-- The elevator arrived at a floor. Check if it took more than 4</span></span>
<span id="cb13-24"><a href="#cb13-24" aria-hidden="true" tabindex="-1"></a>        <span class="co">-- seconds to do so. If not continue and remove the request from</span></span>
<span id="cb13-25"><a href="#cb13-25" aria-hidden="true" tabindex="-1"></a>        <span class="co">-- the pending map.</span></span>
<span id="cb13-26"><a href="#cb13-26" aria-hidden="true" tabindex="-1"></a>        <span class="co">--</span></span>
<span id="cb13-27"><a href="#cb13-27" aria-hidden="true" tabindex="-1"></a>        <span class="dt">ArrivedAt</span> <span class="fu">floor</span> <span class="ot">-&gt;</span></span>
<span id="cb13-28"><a href="#cb13-28" aria-hidden="true" tabindex="-1"></a>          <span class="kw">case</span> Map.lookup <span class="fu">floor</span> pending <span class="kw">of</span></span>
<span id="cb13-29"><a href="#cb13-29" aria-hidden="true" tabindex="-1"></a>            <span class="dt">Nothing</span> <span class="ot">-&gt;</span></span>
<span id="cb13-30"><a href="#cb13-30" aria-hidden="true" tabindex="-1"></a>              checkViolations rest pending</span>
<span id="cb13-31"><a href="#cb13-31" aria-hidden="true" tabindex="-1"></a>            <span class="dt">Just</span> requestTime</span>
<span id="cb13-32"><a href="#cb13-32" aria-hidden="true" tabindex="-1"></a>              <span class="op">|</span> <span class="kw">let</span> time <span class="ot">=</span> t <span class="op">-</span> requestTime</span>
<span id="cb13-33"><a href="#cb13-33" aria-hidden="true" tabindex="-1"></a>                counterexample (  <span class="st">&quot;Passenger waited &quot;</span></span>
<span id="cb13-34"><a href="#cb13-34" aria-hidden="true" tabindex="-1"></a>                               <span class="op">++</span> <span class="fu">show</span> time</span>
<span id="cb13-35"><a href="#cb13-35" aria-hidden="true" tabindex="-1"></a>                               <span class="op">++</span> <span class="st">&quot; for the elevator to arrive to the &quot;</span></span>
<span id="cb13-36"><a href="#cb13-36" aria-hidden="true" tabindex="-1"></a>                               <span class="op">++</span> <span class="fu">show</span> <span class="fu">floor</span></span>
<span id="cb13-37"><a href="#cb13-37" aria-hidden="true" tabindex="-1"></a>                               <span class="op">++</span> <span class="st">&quot; floor&quot;</span></span>
<span id="cb13-38"><a href="#cb13-38" aria-hidden="true" tabindex="-1"></a>                               ) <span class="dt">False</span></span>
<span id="cb13-39"><a href="#cb13-39" aria-hidden="true" tabindex="-1"></a>              <span class="op">|</span> <span class="fu">otherwise</span> <span class="ot">-&gt;</span> checkViolations rest (Map.delete <span class="fu">floor</span> pending)</span>
<span id="cb13-40"><a href="#cb13-40" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb13-41"><a href="#cb13-41" aria-hidden="true" tabindex="-1"></a>        _ <span class="ot">-&gt;</span> checkViolations rest pending</span></code></pre></div>
<p>Then it is just a matter of running the example for randomly generated inputs,
extract the trace and use <code>QuickCheck</code> to assert if the property is true or not.</p>
<div class="sourceCode" id="cb14"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a><span class="ot">prop_no_passenger_waits_4_seconds ::</span> [<span class="dt">Floor</span>] <span class="ot">-&gt;</span> <span class="dt">Property</span></span>
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a>prop_no_passenger_waits_4_seconds floors <span class="ot">=</span></span>
<span id="cb14-3"><a href="#cb14-3" aria-hidden="true" tabindex="-1"></a>  <span class="co">-- Run the button press sequence and get the execution trace</span></span>
<span id="cb14-4"><a href="#cb14-4" aria-hidden="true" tabindex="-1"></a>  <span class="co">--</span></span>
<span id="cb14-5"><a href="#cb14-5" aria-hidden="true" tabindex="-1"></a>  <span class="kw">let</span> trace <span class="ot">=</span> extractElevatorEvents</span>
<span id="cb14-6"><a href="#cb14-6" aria-hidden="true" tabindex="-1"></a>            <span class="op">$</span> runSimTrace</span>
<span id="cb14-7"><a href="#cb14-7" aria-hidden="true" tabindex="-1"></a>            <span class="op">$</span> elevatorExample (<span class="dt">Tracer</span> (emit traceM)) floors</span>
<span id="cb14-8"><a href="#cb14-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb14-9"><a href="#cb14-9" aria-hidden="true" tabindex="-1"></a>   <span class="kw">in</span> violatesFourSecondRule trace</span>
<span id="cb14-10"><a href="#cb14-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb14-11"><a href="#cb14-11" aria-hidden="true" tabindex="-1"></a>  <span class="kw">where</span></span></code></pre></div>
<p>Running this property, <code>QuickCheck</code> quickly finds a counterexample:</p>
<pre><code>*** Failed! Falsified (after 8 tests and 2 shrinks):
[Second,Ground,First]
(Time 0s,ButtonPress Second)
(Time 0s,Going Up Second)
(Time 0s,ButtonPress Ground)
(Time 0s,ButtonPress First)
(Time 2s,ArrivedAt Second)
(Time 2s,Going Down Ground)
(Time 4s,ArrivedAt Ground)
(Time 4s,Going Up First)
(Time 5s,ArrivedAt First)
Passenger waited 5s for the elevator to arrive to the First floor</code></pre>
<p>The counterexample is minimal thanks to <code>QuickCheck</code>’s shrinking. Here, one
can imagine three passengers, pressing a button almost at the same time. Since
the elevator starts on the <code>Ground</code> floor and it is the <code>Second</code> floor passenger
that wins the race, the elevator starts going to the <code>Second</code> floor and queues
the <code>Ground</code> and the <code>First</code> floor requests, by this order. It then takes 5
seconds in total for the elevator to arrive at the <code>First</code> floor, violating
the timeliness requirement.</p>
<p>With property test in place, it is possible to iterate on better algorithms with
confidence. <code>prop_no_passenger_waits_4_seconds</code> property will be able to assert
if any of the improvements actually meet the timing requirements.</p>
<h3 id="using-io-sim-in-the-real-world">Using <code>io-sim</code> in the Real World</h3>
<p>Real systems don’t explicitly block, they perform actual work that takes time.
To make such code testable with <code>io-sim</code>, one can introduce a typeclass abstraction
(e.g. <code>MonadElevator m</code>) with methods like <code>moveElevator</code>. In production,
this would perform real hardware control; in tests, it would use <code>threadDelay</code> to
simulate the operation’s duration.</p>
<p>In this elevator example, in a real system, there would be a sensor which would
inform the controller at what time the elevator arrive at a specific floor, at
which point the internal logic about the current floor of the elevator would be
updated. With suitable abstraction, that implementation could replace our simplification
using <code>threadDelay</code>.</p>
<p><code>io-sim</code> can accurately simulate the standard <code>IO</code> operations, but this additional
abstraction also introduces the challenge of verifying that the model accurately
describes the real-world interactions. For example, 1 second is actually a very
fast elevator, so our model and timeliness requirements may have to be modified
slightly.
That’s a topic left for another blog post!</p>
<h3 id="related-tools-and-libraries">Related Tools and Libraries</h3>
<p>The Haskell ecosystem offers several libraries to test concurrent systems,
each one addresses different aspects of the problem. Here are two of the most
popular and known ones:</p>
<ul>
<li><a href="https://hackage.haskell.org/package/dejafu"><code>dejafu</code></a></li>
<li><a href="https://hackage.haskell.org/package/quickcheck-state-machine"><code>quickcheck-state-machine</code></a></li>
</ul>
<p>Each takes a slightly different approach to exploring thread schedules,
invariants, or state-space, and all have proven useful in practice.</p>
<p><code>dejafu</code> explores all possible thread interleavings to find concurrency bugs.
The library offers a similar typeclass abstraction to <code>io-classes</code> for
concurrency primitives, allowing testing code that uses threads, MVars and
STM.</p>
<p><code>quickcheck-state-machine</code> tests stateful programs using state machine models
with pre and post-conditions. The library can find race conditions through
parallel testing. It excels at testing APIs with complex state dependencies,
e.g. databases or file systems, but focuses on state correctness rather than
temporal properties.</p>
<p><code>io-sim</code> distinguishes itself by being the only time-based simulator. One
can’t easily ask “what happens when this operation takes 150ms instead of
15ms?” with <code>dejafu</code> nor <code>quickcheck-state-machine</code>. <code>io-sim</code> enables testing
of timeout logic, retry mechanisms, timeliness constraints, etc. The ability
to compress years of simulated execution into seconds of test runtime makes
<code>io-sim</code> particularly valuable for testing long-running systems where bugs
emerge only after extended operation.</p>
<h3 id="conclusion">Conclusion</h3>
<p>The key insight is that <code>io-sim</code> simulates the actual behavior of Haskell’s
runtime. STM transactions, thread scheduling, and time passing behave exactly
as in production, but deterministically.</p>
<p>For concurrent Haskell systems with timing requirements, e.g. network
protocols, distributed systems, or real-time controllers, <code>io-sim</code> allows the
verification of time-sensitive properties. The library offers
much more than shown here, including thread scheduling exploration testing with
partial order reduction.</p>
<p>The complete code examples are available <a href="https://github.com/well-typed/verifying-and-testing-with-iosim">here</a>.</p>
]]></content:encoded>
</item>
<item>
    <title>Haskell ecosystem activities report: June–August 2025</title>
    <link>https://well-typed.com/blog/2025/09/haskell-ecosystem-report-june-august-2025</link>
    <description><![CDATA[This is the twenty-eighth 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 2025 to August 2025.

This is a change of name for our GHC [...]]]></description>
    <dc:creator>adam, andreask, ben, hannes, matthew, mikolaj, rodrigo, sam, 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/2025/09/haskell-ecosystem-report-june-august-2025</guid>
    <pubDate>Fri, 26 Sep 2025 00:00:00 GMT</pubDate>
    <content:encoded><![CDATA[<p>This is the twenty-eighth 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 2025 to August 2025.</p>
<p>This is a change of name for our GHC activities report, to reflect the fact that it
focuses on more than just GHC work. 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>Recently we will delighted to <a href="https://well-typed.com/blog/2025/08/standard-chartered-supports-haskell-ecosystem">welcome Standard Chartered as a Gold Haskell
Ecosystem Supporter</a>.
Many thanks to <a href="https://www.sc.com/">Standard Chartered</a>; to our other Haskell Ecosystem Supporters:
<a href="https://www.channable.com/">Channable</a> and <a href="https://qbaylogic.com/">QBayLogic</a>;
to our existing 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="team">Team</h2>
<!-- more -->
<p>The Haskell toolchain team at Well-Typed currently includes:</p>
<ul>
<li><a href="https://well-typed.com/people/andreask">Andreas Klebinger</a></li>
<li><a href="https://well-typed.com/people/ben">Ben Gamari</a></li>
<li><a href="https://well-typed.com/people/matthew">Matthew Pickering</a></li>
<li><a href="https://well-typed.com/people/rodrigo">Rodrigo Mesquita</a></li>
<li><a href="https://well-typed.com/people/sam">Sam Derbyshire</a></li>
<li><a href="https://well-typed.com/people/hannes">Hannes Siebenhandl</a></li>
<li><a href="https://well-typed.com/people/zubin">Zubin Duggal</a></li>
<li><a href="https://well-typed.com/people/mikolaj">Mikolaj Konarski</a></li>
</ul>
<p>In addition, many others within Well-Typed contribute to GHC, Cabal and HLS
occasionally, or contribute to other open source Haskell libraries and tools.
This report includes contributions from <a href="https://well-typed.com/people/alex">Alex Washburn</a> and <a href="https://well-typed.com/people/wen">Wen
Kokke</a> in particular.</p>
<h2 id="ghc">GHC</h2>
<h3 id="ghc-releases">GHC Releases</h3>
<ul>
<li><p>Ben worked on the 9.14.1 release, preparing backports and releasing
the first alpha.</p></li>
<li><p>Ben and Zubin worked on backports for GHC 9.12.3.</p></li>
<li><p>Zubin worked on the 9.10.3 release, preparing backports and publishing
the <code>rc1</code>, <code>rc2</code> and <code>rc3</code> release candidates.</p></li>
</ul>
<h3 id="frontend">Frontend</h3>
<ul>
<li><p>Sam made several improvements to the implementation of deep subsumption,
allowing GHC to accept programs that it previously rejected (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26225">#26225</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14577">!14577</a>).</p></li>
<li><p>Matt refactored the treatment of nested Template Haskell splices in GHC,
making the code paths more consistent and removing code duplication (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14377">!14377</a>).</p></li>
<li><p>Matt fixed some bugs related to level checking (for the <code>ExplicitLevelImports</code> extension),
including a crash in the presence of cyclic imports (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26087">#26087</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14478">!14478</a>) and
some missing level checks (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26088">#26088</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14479">!14479</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26090">#26090</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14550">!14550</a>).</p></li>
<li><p>Andreas allowed the type-class specialiser to look through type families,
exposing more opportunities for specialisation (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26051">#26051</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14272">!14272</a>).</p></li>
<li><p>Sam identified and fixed a situation in which RULES which were not active
could fire nonetheless (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26323">#26323</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14687">!14687</a>).</p></li>
<li><p>Ben and Andreas investigated various performance issues in the
opaque <code>newtype</code> dictionaries patch, in preparation for merging to 9.14 (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/10479">!10479</a>)</p></li>
</ul>
<h3 id="llvm-backend">LLVM backend</h3>
<ul>
<li><p>Alex fixed incorrect sign-extension and narrowing of <code>bitReverse</code>,
<code>byteSwap</code> and <code>pdep</code> primops in the LLVM backend (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/20645">#20645</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26109">#26109</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14609">!14609</a>).</p></li>
<li><p>Alex fixed the LLVM backend generating references to non-existent LLVM
intrinsics <code>llvm.x86.bmi.{pdep,pext}.{8,16}</code>, replacing them with usage
of the appropriate 32-bit operations (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26065">#26065</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14647">!14647</a>).</p></li>
<li><p>Andreas made GHC allow LLVM versions outside of the supported
range, emitting a warning rather than an error (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/25915">#25915</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14531">!14531</a>).</p></li>
<li><p>Ben fixed the treatment of built-in arrays with LLVM, which was necessary
to support newer LLVM versions. This fixed a raft of failing tests with the
LLVM backend (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/25769">#25769</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14157">!14157</a>).</p></li>
<li><p>Ben implemented a major rework of the Windows Clang toolchain (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14442">!14442</a>),
with help from long-time Windows contributor Tamar Christina.
This was necessary to adapt to changes in newer LLVM versions, such as the
use of API sets.</p></li>
</ul>
<h3 id="ghci-and-bytecode-interpreter">GHCi and bytecode interpreter</h3>
<ul>
<li><p>Rodrigo has been working to improve the GHCi debugger, as a step towards
better debugger tooling for Haskell programs. In particular, he implemented
support for stepping-out of a function when debugging in GHCi, with the new
<code>:stepout</code> command (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26042">#26042</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14416">!14416</a>). He also made breakpoint indices 32 bits
instead of 16 bits, as loading large programs in GHCi could overflow the
counter (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26325">#26325</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14691">!14691</a>), and made various other improvements to breakpoints
(<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14461">!14461</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14480">!14480</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14534">!14534</a>).</p></li>
<li><p>Andreas fixed some issues around endianness in the bytecode interpreter, which
could cause the interpreter to produce incorrect results from primitive
operations on some platforms (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/25791">#25791</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/23387">#23387</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14172">!14172</a>).</p></li>
<li><p>Hannes added a new GHCi flag <code>-fno-load-initial-targets</code>, allowing GHCi to be
started without immediately loading all the target modules, so the user can
selectively load the modules they are interested in with <code>:reload</code> (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26144">#26144</a>,
<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14448">!14448</a>). This can significantly reduce GHCi startup times when working on part
of a large project.</p></li>
<li><p>Hannes fixed the remaining issues in the interaction of the GHCi <code>:reload</code>
command with multiple home units (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26128">#26128</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14427">!14427</a>).</p></li>
<li><p>Matt fixed some bugs in interpreter statistics calculations (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/25756">#25756</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/13956">!13956</a>,
<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/25695">#25695</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/13879">!13879</a>).</p></li>
</ul>
<h3 id="documentation">Documentation</h3>
<ul>
<li><p>Hannes updated the user’s guide to advertise full support for multiple home
units (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/20889">#20889</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14426">!14426</a>).</p></li>
<li><p>Wen improved the documentation of eventlog cost centres and sample labels
(<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14499">!14499</a>), and of heap profile IDs (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14506">!14506</a>).</p></li>
<li><p>Andreas added <code>@since</code> annotations for the <code>-fexpose-overloaded-unfolding</code>
and <code>-fdo-clever-arg-eta-expansion</code> GHC flags (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26112">#26112</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26113">#26113</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14517">!14517</a>).</p></li>
</ul>
<h3 id="runtime-system-and-linker">Runtime system and linker</h3>
<ul>
<li><p>Andreas added support for <code>COFF</code> <code>BigObj</code> files in the linker (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14582">!14582</a>).</p></li>
<li><p>Ben made the linker less reliant on file extensions to identify archive
members (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/13103">#13103</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/24230">#24230</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14405">!14405</a>).</p></li>
<li><p>Hannes fixed an oversight in the hashing function used in the RTS (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26274">#26274</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14651">!14651</a>).</p></li>
</ul>
<h3 id="profiling-and-debugging">Profiling and debugging</h3>
<ul>
<li><p>Hannes added a new primop and <code>ghc-experimental</code> API which allows annotating
the call stack with arbitrary data, in pursuit of better backtraces (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26218">#26218</a>,
<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14538">!14538</a>). See his recent blog post
<a href="https://well-typed.com/blog/2025/09/better-haskell-stack-traces">Better Haskell stack traces via user annotations</a>
for a more detailed explanation of the new features.</p></li>
<li><p>Hannes improved the implementation of the <code>Backtraces</code> type and extended the
backtrace mechanism to allow configuring stack decoders (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26211">#26211</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14532">!14532</a>). He
also reverted a change that would have exposed the internal implementation of
the <code>Backtraces</code> type from <code>base</code>, as this needs a CLC proposal (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14587">!14587</a>).</p></li>
<li><p>Andreas disabled the <code>-fprof-late-overloaded-calls</code> functionality for join points,
as this could cause GHC crashes (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26138">#26138</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14460">!14460</a>). This is a temporary fix
before the root problem can be addressed in full.</p></li>
<li><p>Matt allowed info table entries used for profiling to be distributed
separately (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/21766">#21766</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14465">!14465</a>).</p></li>
<li><p>Wen disabled the usage of <code>--eventlog-flush-interval</code> in the non-threaded RTS,
to avoid eventlog corruption (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26222">#26222</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14547">!14547</a>).</p></li>
<li><p>Wen removed the unused hard-coded <code>profile_id</code> from eventlog traces (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14507">!14507</a>).</p></li>
<li><p>Ben factored out the constructor <code>ctoi</code> tuple info tables into a data section
for re-usability (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14508">!14508</a>).</p></li>
<li><p>Ben fixed a regression in <code>zstd</code> compression support for info-table provenance tables (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26312">#26312</a>)</p></li>
</ul>
<h3 id="core-libraries-and-ghc-library">Core libraries and <code>ghc</code> library</h3>
<ul>
<li><p>Zubin added <code>newNameCache</code>, a version of <code>initNameCache</code> that isn’t prone
to being misused (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14446">!14446</a>).</p>
<p>This was in response to a bug in which the <code>weeder</code> library (<a href="https://github.com/ocharles/weeder/pull/194"><code>weeder</code> <span>#194</span></a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26055">#26055</a>)
was using <code>initNameCache</code> incorrectly.</p></li>
<li><p>Rodrigo added an export of <code>displayExceptionWithInfo</code> to <code>base</code>, implementing
<a href="https://github.com/haskell/core-libraries-committee/issues/344">CLC proposal <span>#344</span></a>
(<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14419">!14419</a>).</p></li>
<li><p>Hannes implemented <a href="https://github.com/haskell/core-libraries-committee/issues/212">CLC proposal <span>#212</span></a>,
removing some deprecated heap representation details from <code>GHC.Exts</code> (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14544">!14544</a>).</p></li>
<li><p>Ben removed <code>IOPort</code>, an internal datatype that could be replaced by <code>MVar</code>,
implementing <a href="https://github.com/haskell/core-libraries-committee/issues/213">CLC proposal <span>#213</span></a> (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/8776">!8776</a>).</p></li>
</ul>
<h3 id="build-system-and-packaging">Build system and packaging</h3>
<ul>
<li><p>Zubin fixed an issue in which the user’s guide PDF would not be included
in a binary distribution (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/24093">#24093</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14469">!14469</a>).</p></li>
<li><p>Ben added support for <code>otool</code>, <code>install-name-tool</code> and LLVM utilies such as
<code>llc</code>, <code>opt</code> to <code>ghc-toolchain</code> (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/23675">#23675</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14050">!14050</a>).</p></li>
<li><p>Ben allowed the <code>CrossCompiling</code> predicate to be overridden (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26236">#26236</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14568">!14568</a>).</p></li>
<li><p>Ben dropped build-system logic for preferring the now-deprecated <code>ld.gold</code> linker (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/25716">#25716</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14324">!14324</a>).</p></li>
</ul>
<h3 id="ci-and-testing">CI and testing</h3>
<ul>
<li><p>Hannes and Zubin upgraded the bootstrap compiler to 9.10.1 on MacOS (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14601">!14601</a>),
Windows (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14622">!14622</a>) and FreeBSD (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14666">!14666</a>). This allowed Hannes to update the
<code>test-bootstrap</code> job to use 9.10.1 (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14676">!14676</a>).</p></li>
<li><p>Zubin improved how the testsuite driver filters out certain spurious
linker warnings (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26249">#26249</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14615">!14615</a>).</p></li>
</ul>
<h2 id="haddock">Haddock</h2>
<ul>
<li>Zubin fixed Haddock emitting spurious warnings for undocumented type family
axioms (which cannot have documentation attached to them) (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26114">#26114</a>, <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14447">!14447</a>).</li>
</ul>
<h2 id="cabal">Cabal</h2>
<ul>
<li><p>Matt helped the Cabal project adopt a formal proposal process by writing
up the <a href="https://github.com/haskell/cabal/blob/8b587c30cb450cb8d9a9e7f3d0a0b8c545814e1c/proposals.md">Cabal Proposals Process</a>. This document was
discussed in <a href="https://github.com/haskell/cabal/pull/11006">Cabal <span>#11006</span></a>,
and eventually agreed upon by the existing Cabal maintainers.</p></li>
<li><p>Matt opened a Cabal proposal to add support for bytecode artifacts, which would
speed up GHCi usage and allow the GHCi debugger to step through dependencies
such as code in <code>base</code> (<a href="https://github.com/haskell/cabal-proposals/pull/2">Cabal Proposals <span>#2</span></a>).</p></li>
<li><p>Matt helped finish up work by Cabal contributor Julian G (@jgotoh) to migrate the <code>cabal.project</code>
parser to use <code>Parsec</code> (<a href="https://github.com/haskell/cabal/pull/8889">Cabal <span>#8889</span></a>).</p></li>
<li><p>Matt updated the CI release scripts, bumping the boot compiler version and updating
platforms (<a href="https://github.com/haskell/cabal/pull/11032">Cabal <span>#11032</span></a>).</p></li>
<li><p>Matt adapted the Cabal library to the change in exception contexts in <code>base-4.21</code>
(<a href="https://github.com/haskell/cabal/pull/11125">Cabal <span>#11125</span></a>).
This issue arose when helping Phil de Joux investigate a mysterious issue on
<a href="https://github.com/haskell/cabal/pull/10684">Cabal <span>#10684</span></a>.</p></li>
<li><p>Matt made Cabal use response files when starting multi-repl sessions, rather
than passing long command-line invocations (<a href="https://github.com/haskell/cabal/pull/10995">Cabal <span>#10995</span></a>).
These changes were subsequently reverted for the Cabal 3.16 release, in order
to preserve compatibility with released HLS bindists (<a href="https://github.com/haskell/cabal/pull/11101">Cabal <span>#11101</span></a>).
The plan is to go forward with response files in Cabal 3.18, giving HLS the
time to adapt.</p></li>
<li><p>Matt, with help from Hannes, added the <code>--with-repl</code> flag to the <code>cabal-install repl</code>
command, allowing external tools such as <code>hie-bios</code> and <code>doctest</code> to easily
figure out the correct options for starting a GHCi session (<a href="https://github.com/haskell/cabal/pull/10996">Cabal <span>#10996</span></a>).</p></li>
</ul>
<h2 id="haskell-language-server">Haskell Language Server</h2>
<ul>
<li>Hannes made <code>hie-bios</code> use Cabal’s <code>--with-repl</code> command to load the
session, which greatly simplifies the implementation and its treatment
of multiple home units (<a href="https://github.com/haskell/hie-bios/pull/466"><code>hie-bios</code> <span>#466</span></a>).</li>
</ul>
<h2 id="ghc-debug">ghc-debug</h2>
<ul>
<li><p>Hannes made the IPE information display inline for stack closures (<a href="https://gitlab.haskell.org/ghc/ghc-debug/-/merge_requests/73"><code>ghc-debug</code> <span>#73</span></a>).</p></li>
<li><p>Matthew allowed the <code>ghc-debug-brick</code> terminal interface to be incrementally
updated while a query is run (<a href="https://gitlab.haskell.org/ghc/ghc-debug/-/merge_requests/68"><code>ghc-debug</code> <span>#68</span></a>).</p></li>
<li><p>Hannes restructured the <code>ghc-debug-brick</code> module hierarchy (<a href="https://gitlab.haskell.org/ghc/ghc-debug/-/merge_requests/72"><code>ghc-debug</code> <span>#72</span></a>),
and made it available as a separate library (<a href="https://gitlab.haskell.org/ghc/ghc-debug/-/merge_requests/74"><code>ghc-debug</code> <span>#74</span></a>).</p></li>
<li><p>Hannes added support for custom stack annotations (<a href="https://gitlab.haskell.org/ghc/ghc-debug/-/merge_requests/69"><code>ghc-debug</code> <span>#69</span></a>).</p></li>
</ul>
<h2 id="infrastructure">Infrastructure</h2>
<ul>
<li><p>Ben migrated the <code>haskell.org</code> mail delivery and mailing list infrastructure to
a more maintainable hosting situation. This involved rebuilding the mail delivery
configuration on NixOS, migrating two decades of mailing list data to from <code>mailman-2</code>
to <code>mailman-3</code>, and implementing a scheme to ensure that the previous <code>mailman-2</code>
archives remain available.</p></li>
<li><p>In response to user feedback, Ben carried out a variety of improvements to the
Hackage documentation builder, allowing a greater breadth of packages to build.</p></li>
<li><p>Ben coordinated with the Haskell Foundation to provision a set of new
CI runners for AArch64/Linux to replace capacity lost to the end of
Azure’s open-source program.</p></li>
</ul>
<h2 id="libraries">Libraries</h2>
<ul>
<li><p>Sam migrated the <code>ghc-typelits-natnormalise</code> library to use <code>ghc-tcplugin-api</code>
in <a href="https://github.com/clash-lang/ghc-typelits-natnormalise/pull/90"><code>ghc-typelits-natnormalise</code> <span>#90</span></a>.</p>
<p>This eases the maintenance burden of keeping <code>ghc-typelits-natnormalise</code> working
across different GHC versions. Several pre-existing bugs were fixed in the process
(<a href="https://github.com/clash-lang/ghc-typelits-natnormalise/pull/47"><code>ghc-typelits-natnormalise</code> <span>#47</span></a>,
<a href="https://github.com/clash-lang/ghc-typelits-natnormalise/pull/47"><code>ghc-typelits-natnormalise</code> <span>#70</span></a>,
<a href="https://github.com/clash-lang/ghc-typelits-natnormalise/pull/47"><code>ghc-typelits-natnormalise</code> <span>#71</span></a>).</p></li>
<li><p>Wen improved the <a href="https://hackage.haskell.org/package/eventlog-socket"><code>eventlog-socket</code></a>
library, adding support for setting the eventlog writer from C, and providing
documented usage examples.</p></li>
</ul>
]]></content:encoded>
</item>
<item>
    <title>Better Haskell stack traces via user annotations</title>
    <link>https://well-typed.com/blog/2025/09/better-haskell-stack-traces</link>
    <description><![CDATA[Getting an accurate and precise backtrace is the key to debugging unexpected exceptions in Haskell programs.
We recently implemented a family of functions that enable the user to push user-defined annotations to the native Haskell stack.
The native stack decoder can display this information to the user [...]]]></description>
    <dc:creator>hannes, matthew</dc:creator>
    <category>ghc</category>
    <category>open-source</category>
    <category>exceptions</category>
    <category>debugging</category>
    <guid isPermaLink="false">http://www.well-typed.com/blog/2025/09/better-haskell-stack-traces</guid>
    <pubDate>Thu, 04 Sep 2025 00:00:00 GMT</pubDate>
    <content:encoded><![CDATA[<p>Getting an accurate and precise backtrace is the key to debugging unexpected exceptions in Haskell programs.
We recently implemented a family of functions that enable the user to push user-defined annotations to the native Haskell stack.
The native stack decoder can display this information to the user when an unexpected
exception is thrown.</p>
<p>This facility offers a number of advantages over the existing backtrace collection
mechanisms:</p>
<ul>
<li>It is not necessary modify the function API (unlike <code>HasCallStack</code>)</li>
<li>A “continuous chain” of modifications is not necessary (unlike <code>HasCallStack</code>)</li>
<li>The annotations work in all ways of compilation (unlike cost centre stacks)</li>
<li>The backtrace is expressed in terms of predictable source locations (unlike some IPE backtraces)</li>
</ul>
<p>In this post we wil introduce the API for stack annotation, give some examples of
how to use the annotation functions and discuss some trade-offs we have noticed with the design.</p>
<p>We’re interested in feedback from users on this feature. We’re expecting it
to be available from GHC 9.16, as our implementation already landed in GHC HEAD (<a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14538">!14538</a>).</p>
<h3 id="annotation-stack-frames">Annotation stack frames</h3>
<!-- more -->
<p>The core of the design is a new primop, <code>annotateStack#</code>, which when executed pushes an “annotation stack-frame” to
the stack. Semantically, the frame is a no-op, but the payload contains a pointer to an arbitrary user-defined annotation.
When decoding the native Haskell stack the annotation can be rendered to
provide the user with additional context about the current location of the program.</p>
<p>The primop <code>annotateStack#</code> is exposed to the user via an <code>IO</code>-based API in
<code>GHC.Stack.Annotation.Experimental</code> from the <code>ghc-experimental</code> package:<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></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">annotateStackIO ::</span> (<span class="dt">Typeable</span> a, <span class="dt">StackAnnotation</span> a) <span class="ot">=&gt;</span> a <span class="ot">-&gt;</span> <span class="dt">IO</span> b <span class="ot">-&gt;</span> <span class="dt">IO</span> b</span></code></pre></div>
<p>This will push the annotation value <code>a</code> onto the stack for the duration of the <code>IO b</code> action. The constraints allow the value to be rendered to a string or have its type inspected, similarly to the <code>Exception</code> class.</p>
<p>There are also specialised variants:</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">annotateCallStackIO   ::</span> <span class="dt">HasCallStack</span> <span class="ot">=&gt;</span> <span class="dt">IO</span> b <span class="ot">-&gt;</span> <span class="dt">IO</span> b  <span class="co">-- Annotate with the current source location</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="ot">annotateStackStringIO ::</span> <span class="dt">String</span>       <span class="ot">-&gt;</span> <span class="dt">IO</span> b <span class="ot">-&gt;</span> <span class="dt">IO</span> b  <span class="co">-- Annotate with an arbitrary String</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a><span class="ot">annotateStackShowIO   ::</span> <span class="dt">Show</span> a <span class="ot">=&gt;</span> a  <span class="ot">-&gt;</span> <span class="dt">IO</span> b <span class="ot">-&gt;</span> <span class="dt">IO</span> b  <span class="co">-- Annotate with the result of 'show' on a value</span></span></code></pre></div>
<p>In addition, there are “pure” variants for use in non-<code>IO</code> code. However, these
tend to be less intuitive due to the combination of lazy evaluation and
imprecise exceptions, so the <code>IO</code> versions will generally produce better stack
traces more reliably.</p>
<p>Note, <code>annotateStack#</code> is heavily inspired by <a href="https://hackage.haskell.org/package/annotated-exception"><code>annotated-exception</code></a>
and can be used together with <code>annotated-exception</code> for even better stack traces.</p>
<h3 id="example-of-the-status-quo">Example of the status quo</h3>
<p>Let’s use the annotation functions to improve the backtrace for a program reported
in a GHC ticket (<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26040">#26040</a>).
The program implements a simple REST API using <code>servant</code>. When the endpoint is requested with
a parameter which is larger than or equal to 100, the endpoint will error.
<code>topHandler</code> catches all exceptions thrown by the handler and turns them into an HTTP 505 error.
Finally, the exception handler prints any exceptions that might be thrown by the endpoint.</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="ot">main ::</span> <span class="dt">IO</span> ()</span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a>main <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a>  setBacktraceMechanismState <span class="dt">IPEBacktrace</span> <span class="dt">True</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a>  run <span class="dv">8086</span> mkServer</span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> <span class="dt">Api</span> <span class="ot">=</span> <span class="dt">Capture</span> <span class="st">&quot;x&quot;</span> <span class="dt">Int</span> <span class="op">:&gt;</span> <span class="dt">Get</span> '[<span class="dt">PlainText</span>] <span class="dt">Text</span></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a><span class="ot">mkServer ::</span> <span class="dt">Application</span></span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a>mkServer <span class="ot">=</span></span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a>  serve</span>
<span id="cb3-11"><a href="#cb3-11" aria-hidden="true" tabindex="-1"></a>    (<span class="dt">Proxy</span> <span class="op">@</span><span class="dt">Api</span>)</span>
<span id="cb3-12"><a href="#cb3-12" aria-hidden="true" tabindex="-1"></a>    (hoistServer (<span class="dt">Proxy</span> <span class="op">@</span><span class="dt">Api</span>) topHandler api)</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="ot">topHandler ::</span> <span class="dt">IO</span> a <span class="ot">-&gt;</span> <span class="dt">Handler</span> a</span>
<span id="cb3-15"><a href="#cb3-15" aria-hidden="true" tabindex="-1"></a>topHandler action <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb3-16"><a href="#cb3-16" aria-hidden="true" tabindex="-1"></a>  result <span class="ot">&lt;-</span> liftIO <span class="op">$</span></span>
<span id="cb3-17"><a href="#cb3-17" aria-hidden="true" tabindex="-1"></a>    (<span class="dt">Right</span> <span class="op">&lt;$&gt;</span> action) <span class="ot">`catch`</span> \(<span class="ot">exc ::</span> <span class="dt">SomeException</span>) <span class="ot">-&gt;</span> <span class="kw">do</span></span>
<span id="cb3-18"><a href="#cb3-18" aria-hidden="true" tabindex="-1"></a>      liftIO <span class="op">$</span> <span class="fu">putStrLn</span> <span class="op">$</span> <span class="st">&quot;Exception: &quot;</span> <span class="op">&lt;&gt;</span> displayExceptionWithInfo exc</span>
<span id="cb3-19"><a href="#cb3-19" aria-hidden="true" tabindex="-1"></a>      <span class="fu">pure</span> <span class="op">$</span> <span class="dt">Left</span> err500</span>
<span id="cb3-20"><a href="#cb3-20" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-21"><a href="#cb3-21" aria-hidden="true" tabindex="-1"></a>  <span class="fu">either</span> throwError <span class="fu">pure</span> result</span>
<span id="cb3-22"><a href="#cb3-22" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-23"><a href="#cb3-23" aria-hidden="true" tabindex="-1"></a><span class="ot">api ::</span> <span class="dt">ServerT</span> <span class="dt">Api</span> <span class="dt">IO</span></span>
<span id="cb3-24"><a href="#cb3-24" aria-hidden="true" tabindex="-1"></a>api <span class="ot">=</span> handler</span>
<span id="cb3-25"><a href="#cb3-25" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-26"><a href="#cb3-26" aria-hidden="true" tabindex="-1"></a><span class="ot">handler ::</span> <span class="dt">Int</span> <span class="ot">-&gt;</span> <span class="dt">IO</span> <span class="dt">Text</span></span>
<span id="cb3-27"><a href="#cb3-27" aria-hidden="true" tabindex="-1"></a>handler x <span class="ot">=</span></span>
<span id="cb3-28"><a href="#cb3-28" aria-hidden="true" tabindex="-1"></a>  <span class="kw">if</span> x <span class="op">&gt;=</span> <span class="dv">100</span></span>
<span id="cb3-29"><a href="#cb3-29" aria-hidden="true" tabindex="-1"></a>    <span class="kw">then</span> throw <span class="op">$</span> <span class="dt">ErrorCall</span> <span class="st">&quot;Oh no!&quot;</span></span>
<span id="cb3-30"><a href="#cb3-30" aria-hidden="true" tabindex="-1"></a>    <span class="kw">else</span> <span class="fu">pure</span> (<span class="fu">pack</span> <span class="st">&quot;handler&quot;</span>)</span></code></pre></div>
<p>With the current version of GHC, when calling this API via <code>http://localhost:8086/105</code>, this stack trace is printed:</p>
<pre class="text"><code>Exception: ghc-internal:GHC.Internal.Exception.ErrorCall:

Oh no!

IPE backtrace:
  Main.liftIO (src/Servant/Server/Internal/Handler.hs:30:36-42)
  Servant.Server.Internal.Delayed.runHandler' (src/Servant/Server/Internal/Handler.hs:27:31-41)
  Control.Monad.Trans.Resource.runResourceT (./Control/Monad/Trans/Resource.hs:(192,14)-(197,18))
  Network.Wai.Handler.Warp.HTTP1.processRequest (./Network/Wai/Handler/Warp/HTTP1.hs:195:20-22)
  Network.Wai.Handler.Warp.HTTP1.processRequest (./Network/Wai/Handler/Warp/HTTP1.hs:(195,5)-(203,31))
  Network.Wai.Handler.Warp.HTTP1.http1server.loop (./Network/Wai/Handler/Warp/HTTP1.hs:(141,9)-(157,42))
HasCallStack backtrace:
  collectExceptionAnnotation, called at libraries/ghc-internal/src/GHC/Internal/Exception.hs:170:37 in ghc-internal:GHC.Internal.Exception
  toExceptionWithBacktrace, called at libraries/ghc-internal/src/GHC/Internal/Exception.hs:90:42 in ghc-internal:GHC.Internal.Exception
  throw, called at app/Main.hs:42:10 in backtrace-0.1.0.0-inplace-server:Main</code></pre>
<p>In this example there are two different backtraces:</p>
<ul>
<li>The “IPE backtrace” is constructed by decoding the Haskell stack, using information stored in the binary by <code>-finfo-table-map</code>, where each
frame is automatically associated with a source location. (The compiler option <code>-finfo-table-map</code> was <a href="https://well-typed.com/blog/2021/01/first-look-at-hi-profiling-mode/">originally introduced for profiling</a>.)</li>
<li>On the the other hand, the “<code>HasCallStack</code> backtrace” is built using the implicitly passed <code>HasCallStack</code>
constraints, which are automatically supplied by the type-checker, provided <code>HasCallStack</code> appears in the type.</li>
</ul>
<p>The <code>HasCallStack</code> backtrace seems the most useful, telling us exactly where our program went wrong.
However, the backtrace is very brief, as the rest of the program doesn’t have any <code>HasCallStack</code> constraints.
As such, this stack trace might be unhelpful in larger programs, if the call to <code>error</code> was placed behind
many layers of abstraction.</p>
<p>The IPE backtrace looks impressive, but doesn’t even show us where the exception is thrown!
We get more intermediate source locations, but not the source of the exception.
The function from which the exception is thrown is not even listed.</p>
<p>The reason the IPE backtrace may be unhelpful lies in the way the Haskell call stack works.
We show the IPE info for each stack frame, which doesn’t relate precisely to the original source code and the resulting stack trace feels unintuitive.
One reason for this is many function calls are tail-calls which don’t result in stack frames.</p>
<p>For more of an overview of the different backtrace mechanisms consult the <a href="https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/0330-exception-backtraces.rst#discussion">discussion section of GHC Proposal #330</a>.</p>
<h3 id="better-stack-traces-with-annotatecallstackio">Better stack traces with <code>annotateCallStackIO</code></h3>
<p>The IPE backtrace can be improved by manually annotating important parts of the
program which should always appear in a backtrace.</p>
<p>For example, we always want to know in which handler the exception was thrown in, so
the <code>handler</code> function is annotated with <code>annotateCallStackIO</code>.
Further, we annotate the location where the exception is thrown.</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="ot">handler ::</span> <span class="dt">Int</span> <span class="ot">-&gt;</span> <span class="dt">IO</span> <span class="dt">Text</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a>handler x <span class="ot">=</span> annotateCallStackIO <span class="op">$</span> <span class="kw">do</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a>  <span class="kw">if</span> x <span class="op">&gt;=</span> <span class="dv">100</span></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a>    <span class="kw">then</span> annotateCallStackIO <span class="op">$</span> throw <span class="op">$</span> <span class="dt">ErrorCall</span> <span class="st">&quot;Oh no!&quot;</span></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a>    <span class="kw">else</span> <span class="fu">pure</span> (<span class="fu">pack</span> <span class="st">&quot;handleIndex&quot;</span>)</span></code></pre></div>
<p>When running this program again, the stack trace will now contain the source location of the handler where exception was thrown from:</p>
<pre class="text"><code>Exception: ghc-internal:GHC.Internal.Exception.ErrorCall:

Oh no!

IPE backtrace:
  annotateCallStackIO, called at app/Main.hs:42:10 in backtrace-0.1.0.0-inplace-server:Main
  annotateCallStackIO, called at app/Main.hs:40:13 in backtrace-0.1.0.0-inplace-server:Main
  Main.handler (app/Main.hs:(40,1)-(43,30))
  Main.liftIO (src/Servant/Server/Internal/Handler.hs:30:36-42)
  Servant.Server.Internal.Delayed.runHandler' (src/Servant/Server/Internal/Handler.hs:27:31-41)
  Control.Monad.Trans.Resource.runResourceT (./Control/Monad/Trans/Resource.hs:(192,14)-(197,18))
  Network.Wai.Handler.Warp.HTTP1.processRequest (./Network/Wai/Handler/Warp/HTTP1.hs:195:20-22)
  Network.Wai.Handler.Warp.HTTP1.processRequest (./Network/Wai/Handler/Warp/HTTP1.hs:(195,5)-(203,31))
  Network.Wai.Handler.Warp.HTTP1.http1server.loop (./Network/Wai/Handler/Warp/HTTP1.hs:(141,9)-(157,42))
HasCallStack backtrace:
  collectExceptionAnnotation, called at libraries/ghc-internal/src/GHC/Internal/Exception.hs:170:37 in ghc-internal:GHC.Internal.Exception
  toExceptionWithBacktrace, called at libraries/ghc-internal/src/GHC/Internal/Exception.hs:90:42 in ghc-internal:GHC.Internal.Exception
  throw, called at app/Main.hs:42:32 in backtrace-0.1.0.0-inplace-server:Main</code></pre>
<p>Note the first two entries of the IPE backtrace:</p>
<pre class="text"><code>annotateCallStackIO, called at app/Main.hs:42:10 in backtrace-0.1.0.0-inplace-server:Main
annotateCallStackIO, called at app/Main.hs:40:13 in backtrace-0.1.0.0-inplace-server:Main</code></pre>
<p>These have been added due to our manual annotation of our source program via <code>annotateCallStackIO</code>!</p>
<p>They give us precise source location where the exception is thrown, making the IPE backtrace just as useful
as the <code>HasCallStack</code> backtrace.
However, note, we did not have to change the type signature of <code>handler</code> at all to get a much more informative stack trace.</p>
<h4 id="throwio-vs-throw-vs-error"><code>throwIO</code> vs <code>throw</code> vs <code>error</code></h4>
<p>Some readers may have noticed that we used <code>throw</code> instead of <code>error</code>, which is usually the go to function for throwing
example errors (or from within pure code).
At the moment, <code>throw</code> and <code>error</code> produce noticeably different stack traces, because
<code>error</code> evaluates the exception annotations lazier than <code>throw</code>, which leads
to failing to capture the call stack when throwing the exception. This should be possible to resolve; see GHC issue
<a href="https://gitlab.haskell.org/ghc/ghc/-/issues/25430">#25430</a>.</p>
<p>On the other hand, <code>throwIO</code> behaves more predictably within <code>IO</code> code and the IPE backtrace includes the source location of the exception throwing:</p>
<pre class="text"><code>IPE backtrace:
  Main.handler (app/Main.hs:42:10-45)
  Main.liftIO (src/Servant/Server/Internal/Handler.hs:30:36-42)
  Servant.Server.Internal.Delayed.runHandler' (src/Servant/Server/Internal/Handler.hs:27:31-41)
  Control.Monad.Trans.Resource.runResourceT (./Control/Monad/Trans/Resource.hs:(192,14)-(197,18))
  Network.Wai.Handler.Warp.HTTP1.processRequest (./Network/Wai/Handler/Warp/HTTP1.hs:195:20-22)
  Network.Wai.Handler.Warp.HTTP1.processRequest (./Network/Wai/Handler/Warp/HTTP1.hs:(195,5)-(203,31))
  Network.Wai.Handler.Warp.HTTP1.http1server.loop (./Network/Wai/Handler/Warp/HTTP1.hs:(141,9)-(157,42))</code></pre>
<p>This means that <em>how</em> the exception is thrown is important to get reasonable stack traces.
Unsurprisingly, you should use <code>throwIO</code> whenever you are within the <code>IO</code> monad.</p>
<h3 id="summary">Summary</h3>
<p>Annotation stack frames are a lightweight way to add extra information to stack traces.
By modifying the execution stack, the information is always available and can be used
by the native stack decoder to display informative backtraces to users. We’re
interested to hear what users think about this feature and how libraries will be
adapted to take advantage of the new annotation frames.</p>
<p>This work has been performed in collaboration with <a href="https://mercury.com/">Mercury</a>, who
have a long-term commitment to the scalability and robustness of the Haskell
ecosystem.
Well-Typed are always interested in projects and <a href="https://well-typed.com/ecosystem">looking for funding</a> 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>The <code>ghc-experimental</code> package ships with GHC, but is distinct from <code>base</code>, and has weaker stability guarantees. This allows new APIs to be introduced and fine-tuned before eventually being stabilised and added to <code>base</code>.<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
</ol>
</section>
]]></content:encoded>
</item>

</channel>
</rss>
