{
  "$type": "site.standard.document",
  "bskyPostRef": {
    "cid": "bafyreiblwmuz4h5vkljxbg7zqoulad6xdb5egpzft45rdbrctbuho3j6bm",
    "uri": "at://did:plc:pi6woz4d47bkuws673w2il2r/app.bsky.feed.post/3mgm3lwgxxno2"
  },
  "path": "/t/synthesizing-runtimerep-indexed-type-class-instances-in-a-ghc-plugin-to-simulate-monomorphization/13774#post_1",
  "publishedAt": "2026-03-09T01:23:20.000Z",
  "site": "https://discourse.haskell.org",
  "tags": [
    "GHC.Exts",
    "`Data.Strict.Maybe`",
    "strict",
    "Downen et al., “Kinds are Calling Conventions”",
    "unboxed",
    "“Compiling to Categories”",
    "his version of `Eq`",
    "`EqRep`",
    "cabal file"
  ],
  "textContent": "## Motiviation\n\nGHC Haskell has a nice runtime representation system, centered around `RuntimeRep` and `TYPE :: RuntimeRep -> *` in GHC.Exts, that enables representation-polymorphic abstractions. Unfortunately, this control over representations came late in Haskell’s life, so most of the existing abstractions in the ecosystem (e.g., almost all of the standard type classes in base) work only over `TYPE LiftedRep` (i.e., `*`). I’m interested in creating an alternative foundational library (most likely largely incompatible with the existing ecosystem) to reduce the pain of using unlifted types, for two reasons:\n\n  * To achieve better performance (e.g., avoiding allocation with unboxed types).\n  * To program with strict evaluation (controversial, I know). (Why not just use strictness annotations (or `Strict{,Data}`)? Because, unlike switching from `TYPE LiftedRep` to `TYPE UnliftedRep`, you can’t apply strictness annotations to categorically rule-out bottoms for `data` types, so certain laws on standard type classes don’t hold. E.g., that’s why `Data.Strict.Maybe` from the strict package isn’t an applicative functor; if `Applicative` somehow supported functors with domain `TYPE UnliftedRep`, and if `Data.Strict.Maybe` abstracted over `TYPE UnliftedRep` (instead of over `TYPE LiftedRep` with a strict annotation on the `Just` constructor), then we could have `Applicative Data.Strict.Maybe`.)\n\n\n\n## Problem\n\nThere are a number of obstacles to creating representation-polymorphic abstractions in Haskell, but the one I want to focus on here is that _representations must be “concrete” in certain circumstances_ (details in Downen et al., “Kinds are Calling Conventions”). I think the main two restrictions are as follows:\n\n  * You can’t form a lambda whose parameter has non-concrete representation. E.g., this doesn’t compile:\n\n        foo :: forall (r :: RuntimeRep) (a :: TYPE r). a -> Int\n        foo _ = 0\n\n\n  * You can’t form an application whose argument has non-concrete representation. E.g., this doesn’t compile:\n\n        bar :: forall (r :: RuntimeRep) (a :: TYPE r) b. (a -> b) -> (() -> a) -> b\n        bar f g = f (g ())\n\n\n\n\n\n## Solution Idea\n\nInspired by Edward Kmett’s unboxed experiment and Conal Elliott’s work on “Compiling to Categories”, I’m trying to get around the concreteness restrictions on representation polymorphism with `RuntimeRep`-indexed type classes.\n\nFor example, in conjunction with `DefaultSignatures`, Edward applies this idea to define representation-polymorphic type classes with default method implementations. Look at how his version of `Eq` utilizes an extra `RuntimeRep`-indexed class `EqRep`: the naive default implementation of `(/=)` as `a /= b = not (a == b)` does not satisfy the concreteness constraint on lambdas, because `a` and `b` have non-concrete representation.\n\nThe downside of Edward’s approach is that we must implement each `RuntimeRep`-indexed class (`EqRep`, etc.) for each desired `RuntimeRep` _in advance_. Since there are infinitely-many `RuntimeRep`s (thanks to the recursive `TupleRep` and `SumRep` constructors), this is a losing battle. (Of course, you can automate the instance generation, as Edward does for unboxed tuples and sums of arity 2. See the cabal file.)\n\nIt would be much nicer if the compiler would automatically generate the required instances of `RuntimeRep`-indexed classes _on demand_. So we would, for instance, (1) write `Eq` and `EqRep` exactly as Edward has, (2) write a `RuntimeRep`-uniform implementation “schema” for `EqRep` _once_ , then (3) rely on the compiler to synthesize an instance of `EqRep` from the schema whenever `EqRep r` arises as a constraint with concrete `r :: RuntimeRep`.\n\nI think this is possible with a GHC type checker plugin. I’m not very familiar with GHC internals in general, but I think it could work something like this:\n\n  * User POV:\n\n    1. The user writes a type class `MyClass` and marks it as “`RuntimeRep`-monomorphizable” with an annotation (`ANN` pragma). (If it helps, we could restrict the form of these type classes somewhat; e.g., `class MyClass (r :: RuntimeRep) where ...` probably covers most cases.)\n    2. The user writes an implementation schema for each `MyClass` method. I’m not sure how this should work; it’s probably the hard part.\n    3. The user freely uses `MyClass r` constraints.\n  * Plugin POV:\n\n    1. The plugin looks for classes annotated as `RuntimeRep`-monomorphizable and finds `MyClass` (in this example, anyway; in general, there can be many).\n    2. Each time GHC can’t itself solve a `MyClass r` constraint, it consults the plugin. If `r` is a concrete representation, the plugin synthesizes a instance of `MyClass r` and returns it as evidence. (Hopefully, GHC only consults type checker plugins for constraints it can’t solve itself; otherwise, the plugin has to check if a suitable instance already exists.) (If `r` is _not_ a concrete representation, the plugin does nothing.)\n\n\n\n## Request for Comments/Hints\n\nIs this all a terrible idea? Is there a better way to do it?\n\nIf the suggested solution isn’t too bad, any hints (existing plugins to study, relevant parts of the GHC API to apply, etc.) would be greatly appreciated.",
  "title": "Synthesizing `RuntimeRep`-indexed type class instances in a GHC plugin to simulate monomorphization"
}