Informal Codes Teo Camarasu's web log Zola 2025-10-04T00:00:00+00:00 https://informal.codes/atom.xml Announcing template-haskell-lift and template-haskell-quasiquoter 2025-10-04T00:00:00+00:00 2025-10-04T00:00:00+00:00 Teo Camarasu https://informal.codes/posts/ann-th-lift-and-quasi/ <h2 id="introduction">Introduction<a class="zola-anchor" href="#introduction" aria-label="Anchor link for: introduction">🔗</a></h2> <p>The <code>template-haskell</code> package has a large interface, which includes both stable and unstable parts. The unstable part changes with almost every new version of GHC. This introduces extra work for users of the library who only rely on the stable parts. They now have to update their upper bounds to accommodate the new major version in order to use a new version of GHC.</p> <p>I’m happy to announce the first release of two libraries that expose stable subsets of the <code>template-haskell</code> interface:</p> <ul> <li><code>template-haskell-lift</code>: a stable home for the <code>Lift</code> typeclasss.</li> <li><code>template-haskell-quasiquoter</code>: a stable home for the <code>QuasiQuoter</code> type.</li> </ul> <p>These implement <a href="https://github.com/ghc-proposals/ghc-proposals/pull/696">GHC Proposal 696</a>.</p> <p>By replacing a dependency on <code>template-haskell</code> with one or both of these libraries, users will be more insulated from breaking changes.</p> <p>These smaller interfaces are much less likely to change going forward. If they do change, we will try to release versions that are compatible with multiple major versions of GHC to ease the transition.</p> <p>In some cases, these smaller libraries also allow us to backport new features to older versions of GHC. For instance, GHC-9.16’s <code>template-haskell</code> library will ship with a new <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/12072"><code>defaultQuasiQuoter</code></a> utility, but if you use <code>template-haskell-quasiquoter</code> you will be able to benefit from this immediately.</p> <h2 id="using-template-haskell-lift">Using <code>template-haskell-lift</code><a class="zola-anchor" href="#using-template-haskell-lift" aria-label="Anchor link for: using-template-haskell-lift">🔗</a></h2> <p>Suppose you have a module that defines a datatype <code>Foo</code> and you want to add a <code>Lift</code> typeclass instance.</p> <p>You should add <code>template-haskell-lift</code> to your <code>.cabal</code> file’s <code>build-depends</code> section and then you can derive a <code>Lift</code> instance like so:</p> <pre data-lang="haskell" style="background-color:#2b2c2f;color:#cccece;" class="language-haskell "><code class="language-haskell" data-lang="haskell"><span>{-# </span><span style="color:#c594c5;">LANGUAGE</span><span> DeriveLift #-} </span><span style="color:#c594c5;">module </span><span>Foo </span><span>import Language.Haskell.TH.Lift (</span><span style="color:#c594c5;">Lift</span><span>) </span><span> </span><span>data Foo = ... </span><span> deriving Lift </span></code></pre> <p>If you need to write a custom instance, I would recommend using (Typed) Template Haskell splices, rather than using the TemplateHaskell AST types directly from <code>template-haskell</code>. <code>template-haskell-lift</code> provides compatibility shims for lifting some types that lack appropriate <code>Lift</code> instances.</p> <h2 id="using-template-haskell-quasiquoter">Using <code>template-haskell-quasiquoter</code><a class="zola-anchor" href="#using-template-haskell-quasiquoter" aria-label="Anchor link for: using-template-haskell-quasiquoter">🔗</a></h2> <p>Similarly for <code>QuasiQuoter</code>s, users can add <code>template-haskell-quasiquoter</code> to their <code>build-depends</code> and then use it like so:</p> <pre data-lang="haskell" style="background-color:#2b2c2f;color:#cccece;" class="language-haskell "><code class="language-haskell" data-lang="haskell"><span style="color:#c594c5;">module </span><span>FooQuasi </span><span>import Foo </span><span>import Language.Haskell.TH.QuasiQuoter (</span><span style="color:#c594c5;">QuasiQuoter</span><span style="color:#6699cc;">(..)</span><span style="color:#5fb3b3;">, </span><span style="color:#6699cc;">namedDefaultQuasiQuoter</span><span>) </span><span>import Language.Haskell.TH.Lift (</span><span style="color:#6699cc;">lift</span><span>) </span><span> </span><span>fooQQ :: QuasiQuoter </span><span>fooQQ = namedDefaultQuasiQuoter &quot;fooQQ&quot; {quoteExp = \str -&gt; lift . read @Foo} </span></code></pre> <h2 id="conclusion">Conclusion<a class="zola-anchor" href="#conclusion" aria-label="Anchor link for: conclusion">🔗</a></h2> <p>These libraries should help make it easier to upgrade to a new version of GHC. The more people use them, the greater the benefits will be, so I encourage folks to start using them in their own libraries.</p> <p>For more information about plans to improve the stability of Template Haskell, take a look at my <a href="https://www.youtube.com/watch?v=TCvDq6ScaDg">HEW 2025 talk</a>. For more information about the design of these libraries take a look at <a href="https://github.com/ghc-proposals/ghc-proposals/pull/696">GHC Proposal 696</a>.</p> <p>If you encounter any issues or have suggestions, please open an issue on the respective issue tracker:</p> <ul> <li><a href="https://gitlab.haskell.org/ghc/template-haskell-lift/-/issues">https://gitlab.haskell.org/ghc/template-haskell-lift/-/issues</a></li> <li><a href="https://gitlab.haskell.org/ghc/template-haskell-quasiquoter/-/issues">https://gitlab.haskell.org/ghc/template-haskell-quasiquoter/-/issues</a></li> </ul> Upgrading the CircuitHub codebase to GHC-9.10 2025-08-19T00:00:00+00:00 2025-08-19T00:00:00+00:00 Teo Camarasu https://informal.codes/posts/upgrading-to-ghc-9-10/ <p>We have recently upgraded the CircuitHub codebase from GHC-9.8 to GHC-9.10. Overall it has been a pleasant upgrade experience. This is thanks to the effort that the entire ecosystem has been putting into stability lately, including but not limited to: the Haskell Foundation Stability working group, the GHC steering committee, GHC developers, the core libraries committee, and of course all the individual maintainers. This blog post will list some of the changes we had to make and is inspired by Tom Ellis’s <a href="https://h2.jaguarpaw.co.uk/posts/ghc-8.10-9.6-experience-report/">experience report about upgrading from GHC-8.10 to GHC-9.6</a>.</p> <h2 id="updating-upper-bounds">Updating upper-bounds<a class="zola-anchor" href="#updating-upper-bounds" aria-label="Anchor link for: updating-upper-bounds">🔗</a></h2> <p>The vast majority of the required changes consisted of updating upper bounds on our dependencies. Many packages needed to update their bounds on boot libraries like <code>base</code> or <code>template-haskell</code>. Often no actual changes were required to the library itself.</p> <h2 id="new-exports-from-prelude">New exports from <code>Prelude</code><a class="zola-anchor" href="#new-exports-from-prelude" aria-label="Anchor link for: new-exports-from-prelude">🔗</a></h2> <p><code>base-4.20</code> now exports <code>foldl'</code> from <code>Prelude</code>. This is a welcome change. It has been a footgun that other folds are exported from <code>Prelude</code>, but the one you want most of the time wasn’t.</p> <p>This change led to many redundant import warnings in our project. Like many production codebases, we disallow warnings, so we had to amend many of our modules’ imports to no longer import <code>foldl'</code> from <code>Data.Foldable</code> or <code>Data.List</code>.</p> <p>Changes like this make me wish for the ability to apply HLS code actions to an entire codebase. This would have been resolved by applying the “Remove redundant imports” action followed by a reformat.</p> <h2 id="data-list-nonempty-unzip-warning"><code>Data.List.NonEmpty.unzip</code> warning<a class="zola-anchor" href="#data-list-nonempty-unzip-warning" aria-label="Anchor link for: data-list-nonempty-unzip-warning">🔗</a></h2> <p>In <code>base-4.20</code>, <code>Data.List.NonEmpty.unzip</code> gained a new warning about future monomorphisation (See: <a href="https://github.com/haskell/core-libraries-committee/issues/86">CLC#86</a>). We ended up disabling this warning globally by adding <code>-Wno-x-data-list-nonempty-unzip</code> to the <code>ghc-options</code> in all of our cabal files.</p> <p>We use <code>cabal-fmt</code>’s <code>fragment</code> pragmas, which allows us to share a snippet of code between <code>.cabal</code> files. This allowed us to add this flag to a common stanza, and share it in all of our <code>.cabal</code> files. So this wasn’t much work for us to accommodate.</p> <h2 id="datakind-warnings"><code>DataKind</code> warnings<a class="zola-anchor" href="#datakind-warnings" aria-label="Anchor link for: datakind-warnings">🔗</a></h2> <p>GHC-9.10 added some warnings to do with missing <code>DataKind</code> pragmas. Rather than resolving these individually, we ended up just enabling the <code>DataKind</code> extension globally in our all modules. This was made quite easy again by our use of a <code>cabal-fmt</code> common fragment.</p> <h2 id="addition-of-data-text-show">Addition of <code>Data.Text.show</code><a class="zola-anchor" href="#addition-of-data-text-show" aria-label="Anchor link for: addition-of-data-text-show">🔗</a></h2> <p><code>text-2.1.2</code> exported a new <code>Data.Text.show</code> identifier. Packages that didn’t import <code>Data.Text</code> qualified had to be updated to avoid errors from using <code>show</code>.</p> <h2 id="ghc-source-gen-breakage"><code>ghc-source-gen</code> breakage<a class="zola-anchor" href="#ghc-source-gen-breakage" aria-label="Anchor link for: ghc-source-gen-breakage">🔗</a></h2> <p>The <a href="https://hackage.haskell.org/package/ghc-source-gen"><code>ghc-source-gen</code></a> lets you generate Haskell code by using GHC’s own ASTs. By depending on GHC’s AST, you are always guaranteed to be producing valid syntax. On the other hand, depending on GHC internals means that it breaks almost every release and requires significant amounts of CPP. The package was fixed by Ian-Woo Kim in <a href="https://github.com/google/ghc-source-gen/pull/117">this</a> PR.</p> <p>Given that this package is likely to break in the future, the best approach might be to not depend on it if possible. For instance the popular <code>proto-lens</code> library makes use of this, and if the maintainers are willing then it would be great if we could rewrite its Haskell generation to not use <code>ghc-source-gen</code>. See <a href="https://github.com/google/proto-lens/issues/512"><code>proto-lens#512</code></a>.</p> <p>It has also been identified in the Facundo Domínguez’s GHC API stability <a href="https://docs.google.com/document/d/1eYkEWksPNAXPyj3gsmtYa3ycLx9XssUEhluoy2nuMFw/edit?tab=t.0#heading=h.3whi40jnv4u5">work</a>, and that might lead to some improvements.</p> <h2 id="hie-bug">HIE bug<a class="zola-anchor" href="#hie-bug" aria-label="Anchor link for: hie-bug">🔗</a></h2> <p>When upgrading <code>weeder</code> to use the new compiler, we discovered a <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/26055">bug</a> in its parsing of HIE files. Zubin Duggal investigated and it turned out to be a bug in <code>weeder</code>’s usage of the HIE API rather than a bug in GHC.</p> <h2 id="conclusion">Conclusion<a class="zola-anchor" href="#conclusion" aria-label="Anchor link for: conclusion">🔗</a></h2> <p>This was a relatively easy GHC upgrade. Most of the changes were minor and just had to do with imports.</p> <p>Before trying to upgrade to GHC-9.12, I will try to see if we can remove <code>ghc-source-gen</code> from our dependency closure. Any package like this which is tightly coupled to the GHC API is likely to need updating after every release. Reducing one’s dependency footprint and removing fragile package is often a good way to make future upgrades easier.</p> <p>From some cursory attempts to get GHC-9.12 working, it sounds like that upgrade will be even easier.</p> HIW 2025 Talk: Catching space leaks at compile-time using th-deepstrict 2025-06-06T00:00:00+00:00 2025-06-06T00:00:00+00:00 Teo Camarasu https://informal.codes/talks/hiw25/ <p>I gave a talk entitled <em>Catching space leaks at compile-time using <code>th-deepstrict</code></em> at the Haskell Implementors Workshop 2025.</p> <p>You can find the slides <a href="https://informal.codes/talks/hiw25/./th-deepstrict-hiw-teo.pdf">here</a> and watch the recording on YouTube <a href="https://www.youtube.com/watch?v=U2quIEF9R-E">here</a>.</p> HEW 2025 Talk: Template Haskell as a Case Study in (In)stability 2025-06-05T00:00:00+00:00 2025-06-05T00:00:00+00:00 Teo Camarasu https://informal.codes/talks/hew25/ <p>I gave an invited talk entitled <em>Template Haskell as a Case Study in (In)stability</em> at the Haskell Ecosystem Workshop 2025.</p> <p>You can find the slides <a href="https://informal.codes/talks/hew25/./teo-th-hew25.pdf">here</a> and watch the recording on YouTube <a href="https://www.youtube.com/watch?v=TCvDq6ScaDg">here</a>.</p> Working towards a more stable Template Haskell 2024-05-06T00:00:00+00:00 2024-05-06T00:00:00+00:00 Teo Camarasu https://informal.codes/posts/stabilising-th/ <p>Template Haskell enables writing programs that generate and manipulate Haskell code. This is a powerful tool that lets us avoid boilerplate and expand the expressivity of the language. Yet, its power comes with a risk. Users of Template Haskell often end up being tightly coupled to the <code>template-haskell</code> support library. This library defines Template Haskell’s version of the Haskell syntax tree and the interface between Template Haskell and GHC. Almost every release of GHC leads to a breaking change to this library to reflect the ever expanding range of syntax accepted by GHC. These changes often break user code. Over time libraries that use <code>template-haskell</code> amass a bunch of conditionally compiled code to enable compatibility with a wide range of <code>template-haskell</code> versions.</p> <p>This post is about some work I have been doing with Sebastian Graf and other GHC devs to help avoid Template Haskell breaking user’s code in the future. This is a well known problem (see <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/24021">GHC ticket #24021</a>). There is no simple, single solution. Both the API and its patterns of usage are complex.</p> <p>So far, our focus has been on the internal relationship between GHC and the <code>template-haskell</code> library. We managed to untie some knots that tightly coupled together each version of GHC to their bundled version of <code>template-haskell</code>, and knots that made it difficult to change the interface of <code>template-haskell</code>. With these blockers out of the way, it should be possible to make some changes to give us much stronger stability guarantees.</p> <p>Before embarking on implementing these exciting improvements, we wanted to share our plans to get feedback and to invite the community to collaborate with us.</p> <p>I’ll sketch how Template Haskell leads to instability, the changes we are planning to make, the changes we’ve made so far and how you can get involved.</p> <h2 id="why-template-haskell-leads-to-broken-code">Why Template Haskell leads to broken code<a class="zola-anchor" href="#why-template-haskell-leads-to-broken-code" aria-label="Anchor link for: why-template-haskell-leads-to-broken-code">🔗</a></h2> <p>At the very core of the <code>template-haskell</code> library is a set of data types that represent Haskell syntax trees: <code>Expr</code>, <code>Type</code>, <code>Dec</code>, <code>Pat</code>, etc. These types often change, because they reflect the source-syntax of GHC Haskell, in its full glory. That in turn means that any code that directly uses these data types (for construction or pattern matching) will break as new releases of GHC change the source-syntax. We want to loosen this undesirable coupling between the source-syntax and user code.</p> <p>We can divide usages of Template Haskell by how they relate to these syntax tree types. At a high level, a specific usage either produces/constructs or consumes/deconstructs these types. We can further subdivide these into four categories:</p> <ul> <li>(A) Producing with quotes</li> <li>(B) Producing with constructors or smart constructors</li> <li>(C) Consuming through reification</li> <li>(D) Consuming for syntax analysis</li> </ul> <p>A library that makes use of Template Haskell is likely to have a mixture of these usage patterns. For instance, a common case is a library that produces derived typeclass instances. Such a library might use reification (type C) to look up the definition of a data type, then use smart constructors (type B) to generate the definition of the instances themselves.</p> <p>Let’s now briefly look at each of these usage classes, and their bearing on user code stability.</p> <h3 id="producing-syntax-using-constructors-unstable-but-popular-type-b">Producing syntax using constructors: unstable, but popular (type B)<a class="zola-anchor" href="#producing-syntax-using-constructors-unstable-but-popular-type-b" aria-label="Anchor link for: producing-syntax-using-constructors-unstable-but-popular-type-b">🔗</a></h3> <p>By far the most popular way to produce Template Haskell syntax trees is by either directly using constructors (<code>VarE</code>), or by using the monadic smart constructors (<code>varE</code>) exported by <a href="https://hackage.haskell.org/package/template-haskell-2.22.0.0/docs/Language-Haskell-TH-Lib.html"><code>Language.Haskell.TH.Lib</code></a>. These both make users vulnerable to breakage when the syntax tree types change.</p> <p>For instance, in <code>template-haskell-2.18</code>, the <code>ConP</code> constructor, which represents a constructor pattern was given a new field to represent the possibility of a list of type applications preceding the argument. This led to the following <a href="https://github.com/bitemyapp/esqueleto/blob/30a5e80736391e2aa45094f681d4bd329aa16707/src/Database/Esqueleto/Record.hs#L541">code</a> in <code>esqueleto</code> to break:</p> <pre data-lang="hs" style="background-color:#2b2c2f;color:#cccece;" class="language-hs "><code class="language-hs" data-lang="hs"><span>pure </span><span style="color:#5fb3b3;">$ </span><span>ConP &#39;Value [VarP var] </span></code></pre> <p>This then had to be patched to give an empty value for the new field (and conditional compilation had to be introduced to support both versions):</p> <pre data-lang="hs" style="background-color:#2b2c2f;color:#cccece;" class="language-hs "><code class="language-hs" data-lang="hs"><span>pure </span><span style="color:#5fb3b3;">$ </span><span>ConP &#39;Value </span><span style="color:#f99157;">[]</span><span> [VarP var] </span></code></pre> <p>By using these constructors or smart constructors directly to produce syntax trees, users expose themselves to breakage whenever a new field is added. And as we’ve pointed out, these happen often.</p> <h3 id="producing-syntax-using-quotes-stable-but-unpopular-type-a">Producing syntax using Quotes: stable, but unpopular (type A)<a class="zola-anchor" href="#producing-syntax-using-quotes-stable-but-unpopular-type-a" aria-label="Anchor link for: producing-syntax-using-quotes-stable-but-unpopular-type-a">🔗</a></h3> <p>Template Haskell quotes produce syntax trees in a very stable manner. Haskell’s concrete syntax develops in a backwards compatible way. Changes to the language will not cause <code>x + y</code> to stop being accepted, and neither will it break <code>[| x + y |]</code>, irrespective of whether the representation of infix operations in the Template Haskell syntax tree types changes. By using quotes, we can piggy-back off these existing backwards compatibility guarantees.</p> <p>Returning to our example from before, we could have expressed the same thing using quote syntax as:</p> <pre data-lang="hs" style="background-color:#2b2c2f;color:#cccece;" class="language-hs "><code class="language-hs" data-lang="hs"><span style="color:#c594c5;">[p|</span><span style="color:#99c794;"> Value $(varP var) </span><span style="color:#c594c5;">|] </span></code></pre> <p>This wouldn’t have broken, and is unlikely to ever break with a newer version of <code>template-haskell</code>.</p> <p>Yet, quotes are much less popular than direct uses of constructors. There are definitely some places where quotes (currently) fall short and we are trying to track these <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/24783#splicing-quoting-lifting-th-ast-and-api-parity">here</a>. These don’t completely explain their low uptake though. The example above shows one place where they could’ve been used to make code more stable, but weren’t.</p> <p>Next time you are writing Template Haskell code, try using quotes. If you run into issues let us know on the GHC bug tracker, whether they are to do with limitations in expressivity, error messages, or lack of documentation.</p> <h3 id="consuming-syntax-through-reification-type-c">Consuming syntax through reification (type C)<a class="zola-anchor" href="#consuming-syntax-through-reification-type-c" aria-label="Anchor link for: consuming-syntax-through-reification-type-c">🔗</a></h3> <p><code>template-haskell</code> exports a <code>reify</code> family of methods to lookup information about identifiers.</p> <p>For instance:</p> <pre data-lang="hs" style="background-color:#2b2c2f;color:#cccece;" class="language-hs "><code class="language-hs" data-lang="hs"><span style="color:#6699cc;">reifyType </span><span style="color:#c594c5;">:: Name -&gt; Q Type </span></code></pre> <p>Here <code>reifyType</code> will return the type of the given identifier. But the notion of type in play is that of source-language type syntax, which is full of syntactic clutter like parentheses, infix operators, list and tuple notation, etc. For reification we don’t want source syntax; we want a nice small representation of the type (type variable, arrow, forall, type constructor application etc). This is simply a mis-design of Template Haskell’s reification interface.</p> <p>Not only does this get in the way when we want to analyse the results from reification functions, since we need to wade through the details of source-language syntax, but we are also exposed to breaking changes from when these types often change. Returning to the <code>ConP</code> example, if we pattern matched on a <code>ConP</code> constructor, our code would break when the new field was added.</p> <h3 id="consuming-syntax-for-analysis-type-d">Consuming syntax for analysis (type D)<a class="zola-anchor" href="#consuming-syntax-for-analysis-type-d" aria-label="Anchor link for: consuming-syntax-for-analysis-type-d">🔗</a></h3> <p>Exhaustively pattern matching on Template Haskell syntax trees is the rarest usage pattern. Users might for instance want to find all free variables, or transform the entire syntax tree into a different representation.</p> <p>Such users are inherently tightly coupled to the exact definition of syntax trees for a given version of GHC. Because this case is both much more difficult and much more rare than the others, we currently aren’t focused on avoiding breaking changes here. Indeed it’s hard to see how that could be possible.</p> <h2 id="how-we-can-avoid-tight-coupling">How we can avoid tight coupling<a class="zola-anchor" href="#how-we-can-avoid-tight-coupling" aria-label="Anchor link for: how-we-can-avoid-tight-coupling">🔗</a></h2> <p>Now we move from the current situation to the ways we can improve it. In this section I will be summarising the <a href="https://gitlab.haskell.org/ghc/ghc/-/wikis/template-haskell/Plan-to-Stabilise-Template-Haskell">Plan to Stabilise Template Haskell</a> document I’ve written up, which in turn is trying to synthesise the various ideas by GHC developers and others into a concrete plan.None of these plans are set in stone, and they could benefit from feedback and experimentation.</p> <p>The tight coupling we have sketched consists of two components: (i) the definitions of the syntax tree types changing regularly with new versions of GHC; (ii) user code depends on the exact definitions of the syntax tree types. When these two elements combine, code breaks. So, our aim is to as much as possible reduce the prevalence of one (or both) of these.</p> <h3 id="loosening-coupling-between-ghc-and-template-haskell">Loosening coupling between GHC and <code>template-haskell</code><a class="zola-anchor" href="#loosening-coupling-between-ghc-and-template-haskell" aria-label="Anchor link for: loosening-coupling-between-ghc-and-template-haskell">🔗</a></h3> <p>Of the usage patterns we’ve examined, only one, type D, cares about seeing the entire, exact syntax trees that correspond to GHC’s current version of the source-language. All of the other use cases can be satisfied with a mere subset of the language. This opens up a possibility that allows us to avoid breaking changes for the vast majority of users, without requiring any modifications to their usage of Template Haskell. The idea is to let a user use a version of <code>template-haskell</code> whose syntax potentially corresponds to the source-language of an older GHC. In so doing, we can decouple releases of GHC and <code>template-haskell</code>, and have it make breaking changes on its own schedule and implement migration plans.</p> <p>For instance, let’s again examine the <code>ConP</code> breaking change. This came out in <code>template-haskell-2.18</code> bundled with <code>GHC-9.2</code>. But consider if we had released a minor version of <code>template-haskell-2.17</code> that insulated against this change by exporting a pattern synonym that defaulted the new field always to <code>[]</code>, and ignored the field when pattern matching. Then users could have upgraded their GHC without even having to change the bound on their <code>template-haskell</code> dependency and their code would have continued to compile. This strategy wasn’t actually possible to implement at the time due to some blockers that we’ve only recently cleared.</p> <p>This sort of migration strategy would work for type A, B, and C clients, but cause issues for type D clients, who explicitly want the latest syntax tree. We would have to enable some way for them to access this without any compatibility shims, perhaps through a different <code>template-haskell-unstable</code> package.</p> <p>This example is only one of the possibilities amongst many of how this migration could have been implemented. Another option would be to rename the constructor when adding the new field and keep the name <code>ConP</code> for a compatibility pattern synonym that matches the old definition.</p> <p>How exactly we implement these should be decided on a case by case basis. The important detail is that by decoupling <code>template-haskell</code> and GHC, we introduce slack into the relationship that makes these sorts of gradual migration plans possible.</p> <p>Any sudden ecosystem migration is a great burden on maintainers, and can be slow to fully implement. Therefore this strategy is very attractive because it requires no work for downstream maintainers. This complements greater usage of quotes well. Quotes still should probably be used for new Template Haskell code. But by implementing something like this, we avoid putting maintainers into a double bind where they either need to quickly rewrite their code into a different style or suffer from breaking changes. This would give time for the transition to happen more gradually.</p> <p>These compatibility shims could likely not be maintained indefinitely, so users would benefit from a sliding window of compatibility. They would still need to upgrade their <code>template-haskell</code> package bounds eventually.</p> <p>This naturally leads to another strategy. We can also expose copies of the syntax tree types that correspond to some well-defined version of the language like <code>Haskell2010</code>. If users move to depending on these interfaces then they can get even stronger stability guarantees. As we would have a moving window of support for GHC versions for each version of the <code>template-haskell</code> interface, but we could support these well-defined versions almost indefinitely. The trade-off is that this would require modifications to downstream code, but still much less modification than rewriting one’s code to use quotes throughout.</p> <h3 id="loosening-coupling-between-template-haskell-and-user-code">Loosening coupling between <code>template-haskell</code> and user code<a class="zola-anchor" href="#loosening-coupling-between-template-haskell-and-user-code" aria-label="Anchor link for: loosening-coupling-between-template-haskell-and-user-code">🔗</a></h3> <p>We plan to improve the interface of Template Haskell, so that users aren’t forced into tight coupling.</p> <p>We are hoping to find the pain points and improve the experience of using Template Haskell quotes. Then users could benefit both from this nicer syntax and the stability benefits it would bring. For instance, we are looking at adding <a href="https://github.com/ghc-proposals/ghc-proposals/pull/529">(untyped) pattern quotes</a> that would allow one to pattern match on Template Haskell syntax trees using quotes.</p> <p>We are planning to improve the experience of deconstructing syntax tree values by introducing named record fields. These would allow users to only look at the fields they care about and mean that their pattern matching code wouldn’t break when new fields are added. This would primarily help type D clients, but would also benefit type C clients who occasionally want to pattern match shallowly on Template Haskell syntax trees.</p> <p>To improve the experience when <code>reify</code> and friends, we are planning to transition these methods to return types that return some sort of normalised types rather than the syntax tree types. These would be closer to GHC’s Core or System F rather than surface syntax. This is what users want anyway when performing analysis. For instance libraries like <a href="https://hackage.haskell.org/package/th-abstraction"><code>th-abstraction</code></a> and <a href="https://hackage.haskell.org/package/th-desugar"><code>th-desugar</code></a> already implement forms of normalisation (to differing degrees), and express an appetite for this. We can draw on these libraries to guide our design.</p> <h2 id="what-s-happened-so-far">What’s happened so far<a class="zola-anchor" href="#what-s-happened-so-far" aria-label="Anchor link for: what-s-happened-so-far">🔗</a></h2> <p>My efforts so far have been focused on clearing roadblocks that have blocked changes like these in the past. I have merged two MRs into GHC: <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/12306">!12306</a> and <a href="https://gitlab.haskell.org/ghc/ghc/-/merge_requests/12479">!12479</a>. These have benefited greatly from reviews and collaboration from Sebastian Graf, Matthew Craven and others. Collectively these two change the relationship between GHC and <code>template-haskell</code>, so that it becomes a library just like any other.</p> <p>The first of these changes the way the bootstrapping of Template Haskell works. Historically, the process required that the interface of the new <code>template-haskell</code> library matched the interface of the <code>template-haskell</code> library that came with the bootstrapping compiler in certain ways. This made it difficult to refactor <code>template-haskell</code> (see <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/23536">#23536</a>). Small changes could lead to difficult to debug errors. We resolved this through a form of vendoring, allowing two versions of <code>template-haskell</code> in essence to co-exist at the same time. Thus removing the requirement.</p> <p>The second change moves any identifiers that have a special wired-in status to the <code>ghc-internal</code> package, mirroring the changes to <code>base</code>. This then meant that <code>template-haskell</code> can become a completely normal package that just happens to be where certain wired-in identifiers are conventionally re-exported from.</p> <p>With these changes implemented, the ideas I sketched out in the previous are now much more easily achievable.</p> <h2 id="how-you-can-get-involved">How you can get involved<a class="zola-anchor" href="#how-you-can-get-involved" aria-label="Anchor link for: how-you-can-get-involved">🔗</a></h2> <p>This is a great stage to get involved. I would love to hear feedback on the plan I’ve sketched, and if you have time on the <a href="https://gitlab.haskell.org/ghc/ghc/-/wikis/template-haskell/Plan-to-Stabilise-Template-Haskell">Plan to Stabilise Template Haskell</a> document.</p> <p>I would especially like to hear about concrete examples where Template Haskell quotes have been lacking to help guide us to improve this feature. We feel like Template Haskell quotes are vastly underutilised and would love to help remove barriers to people adopting them. We are trying to gather up issues <a href="https://gitlab.haskell.org/ghc/ghc/-/issues/24783#splicing-quoting-lifting-th-ast-and-api-parity">here</a>.</p> <p>It would also be great to see more people get involved in this process, and maybe help prototype some of these ideas. You don’t have to be a confident GHC developer to take part!</p> <p>A few of us will be at ZuriHac 2024 where we will be working on improving Template Haskell. Come join us! Sebastian has collated <a href="https://informal.codes/posts/stabilising-th/%5Bhttps://gitlab.haskell.org/ghc/ghc/-/issues/24783">some tickets</a> about improving Template Haskell (not all stability related).</p> <p>The future is looking bright for Template Haskell. I’m really hoping that very soon we will be able to give strong guarantees to users that upgrading GHC won’t lead to any breakages caused by Template Haskell.</p> <h2 id="acknowledgments">Acknowledgments<a class="zola-anchor" href="#acknowledgments" aria-label="Anchor link for: acknowledgments">🔗</a></h2> <p>Thanks to Adam Gundry, Sebastian Graf, Simon Peyton Jones, and Michael Peyton Jones for reading and commenting on a draft of this post.</p>