#GADT
You can now declare arbitrary indexed families, such Vec(n), Expr(t), Matrix(rows, columns), or Term(context, type). Family parameters form dependent telescopes, so a later parameter may depend on an earlier one.
September 12, 2026 at 8:08 PM
We have new versions of panproto (0.74) and didactic (0.15). didactic adds a public general first-order generalized algebraic data type (GADT) and indexed-family language, and panproto checks the resulting theory.
GitHub - panproto/didactic: A typed data library for Python on top of panproto.
A typed data library for Python on top of panproto. - panproto/didactic
github.com
September 12, 2026 at 8:08 PM
Nullary operations on GADT with constraints
I am trying to define something similar to `Data.Monoid.Ap` but as GADT with constraints on the constructors: import Data.Monoid (Ap(..)) -- instance (Monoid a, Applicative f) => Monoid (Ap f a) data Lifted f a where Lift :: (Monoid a, Applicative f) => Ap f a -> Lifted f a One can have a `Semigroup` instance wherein the constraints are hidden: instance Semigroup (Lifted f a) where (Lift a) <> (Lift b) = Lift (a <> b) This works because by matching on the `Lift` constructor, the constraints necessary for the `Semigroup (Ap f a)` instance are brought into scope. However, this approach does **not** work for nullary operations like `mempty`, because there is nothing to pattern-match on. Can it be done nevertheless? Perhaps with proxies? A possible work-around would be to introduce a second constructor taking a rank-2 argument: data Lifted f a where Lift :: (Monoid a, Applicative f) => Ap f a -> Lifted f a Constant :: (forall x. Monoid x => x) -> Lifted f a instance Monoid (Lifted f a) where mempty = Constant mempty The drawback is combinatorial explosion in the definition of the semigroup operation (or any other n-ary operation). Instead of one case, it now must handle four. The class `Monoid` is only an example. My question applies to all classes of algebraic structures that have constants.
discourse.haskell.org
September 11, 2026 at 9:48 AM
Rust compiles GADT-style enums to zero-cost assembly—no interpreter loop, no allocation, just leaq and addq for expression trees with lambdas.
Zero-Cost 'Tagless Final' in Rust with GADT-style Enums
Four Signals — The Wire
www.foursignals.dev
August 29, 2026 at 7:45 AM
RustでGADTを用いたTagless Initialエンコーディングを実装
エンジニアへの影響:コンパイラの最適化によりゼロコストで計算結果のみの機械語へ展開可能
https://inferara.com/blog/rust-tagless-final-gadt/
Zero-Cost 'Tagless Final' in Rust with GADT-style Enums
A deep dive into implementing the 'tagless initial' pattern in Rust using enums and the never type to achieve zero-cost abstractions, demonstrated with optimized assembly output.
inferara.com
August 28, 2026 at 1:00 PM
Zero-Cost 'Tagless Final' in Rust with GADT-style Enums
Discussion | lobsters | Author: abhin4v

#Rust
Zero-Cost 'Tagless Final' in Rust with GADT-style Enums
A deep dive into implementing the 'tagless initial' pattern in Rust using enums and the never type to achieve zero-cost abstractions, demonstrated with optimized assembly output.
inferara.com
August 28, 2026 at 10:55 AM
Zero-Cost 'Tagless Final' in Rust with GADT-style Enums
Comments
inferara.com
August 29, 2026 at 1:41 AM
Gadt damb. 🤤
August 14, 2026 at 12:45 AM
Derived data family instance
That is essentially the approach I am testing currently. From the GHC.Generics module I extracted these key design points: 1. All constructors that you would normally put in a data family or GADT get their own data type, like `:+:` or `:*:`. 2. The helper class (`Generic` in that case) has an associated type family, which is not injective, but… 3. … still allows one to gain _some_ information about the source type by pattern matching on the constructors of the type family target. 4. Due to non-injectivity, all further derived instances (e.g. `Eq`) must work on the dedicated types from (1) 5. The _derived family instance_ can be implemented using the same mechanism that the `Generically` newtype is offering. This corresponds to your `MkTG` constructor. The only drawback is that this can pollute the type family target type with yet another newtype wrapper. AntC2: > I don’t pretend to understand what you’re trying to do My use case is a representation of an (externally specified) domain-specific language (DSL). There are more things to do with it, but the basic thing is evaluation: type Semantics DSL a = (a -> Bool) -- concrete semantics is irrelevant runDSL :: DSL a -> Semantics (DSL a) The naive approach would be to just encode the AST. newtype DSL a = DSLExpr {getAST :: AST} However, not all expressions might be valid for all types. Hence we must have typeCheckDSL :: AST -> Maybe (Semantics DSL a) If that sort of run-time failure is not desired, one would traditionally reach for a GADT. data DSL a where -- many GADT constructors typeCheckDSL :: AST -> Maybe (DSL a) runDSL :: DSL a -> (Semantics DSL a) -- now type safe Now the type mismatch can be thrown at parsing stage. However, the GADT is _closed_ while the basic library may not want to cover the DSL spec for _all Haskell types_. In contrast a type class is open and extensible. class RunDSL a where type DSL a typeCheckDSL :: AST -> Maybe (DSL a) runDSL :: DSL a -> (Semantics DSL a) My goal is now to provide instances for a wide range of types without much boilerplate. To that end, we provide re-usable `DSL a` building blocks as separate types, like the ones used in a Generic `Rep`. If we covered all `Rep` types in our DSL class, then we can have the desired derived instance: newtype GenericDSL a = GenericDSL (forall p. DSL (Rep a p)) If we now declare for a concrete type deriving via (Generically T) instance RunDSL T then the `DSL T` type would have the `GenericDSL` newtype wrapper as single constructor, witnessing the fact that we used a derived instance. But that is okay, since the main interface for construction is `typeCheckDSL` which can add this wrapper automatically. My implementation is not complete enough to see whether the non-injectivity of the `DSL` type family leads to road blocks that weren’t there with a GADT. Anyways, thanks a lot for giving so much thought to this problem of mine.
discourse.haskell.org
August 10, 2026 at 9:23 AM
Derived data family instance
That is essentially the approach I am testing currently. From the GHC.Generics module I extracted these key design points: 1. All constructors that you would normally put in a data family or GADT get their own data type, like `:+:` or `:*:`. 2. The helper class (`Generic` in that case) has an associated type family, which is not injective, but… 3. … still allows one to gain _some_ information about the source type by pattern matching on the constructors of the type family target. 4. Due to non-injectivity, all further derived instances (e.g. `Eq`) must work on the dedicated types from (1) 5. The _derived family instance_ can be implemented using the same mechanism that the `Generically` newtype is offering. This corresponds to your `MkTG` constructor. The only drawback is that this can pollute the type family target type with yet another newtype wrapper. AntC2: > I don’t pretend to understand what you’re trying to do My use case is a representation of an (externally specified) domain-specific language (DSL). There are more things to do with it, but the basic thing is evaluation: type Semantics DSL a = (a -> Bool) -- concrete semantics is irrelevant runDSL :: DSL a -> Semantics (DSL a) The naive approach would be to just encode the AST. newtype DSL a = DSLExpr {getAST :: AST} However, not all expressions might be valid for all types. Hence we must have typeCheckDSL :: AST -> Maybe (Semantics DSL a) If that sort of run-time failure is not desired, one would traditionally reach for a GADT. data DSL a where -- many GADT constructors typeCheckDSL :: AST -> Maybe (DSL a) runDSL :: DSL a -> (Semantics DSL a) -- now type safe Now the type mismatch can be thrown at parsing stage. However, the GADT is _closed_ while the basic library may not want to cover the DSL spec for _all Haskell types_. In contrast a type class is open and extensible. class RunDSL a where type DSL a typeCheckDSL :: AST -> Maybe (DSL a) runDSL :: DSL a -> (Semantics DSL a) My goal is now to provide instances for a wide range of types without much boilerplate. To that end, we provide re-usable `DSL a` building blocks as separate types, like the ones used in a Generic `Rep`. If we covered all `Rep` types in our DSL class, then we can have the desired derived instance: newtype GenericDSL a = GenericDSL (forall p. DSL (Rep a p)) If we now declare for a concrete type deriving via (Generically T) instance RunDSL T then the `DSL T` type would have the `GenericDSL` newtype wrapper as single constructor, witnessing the fact that we used a derived instance. But that is okay, since the main interface for construction is `typeCheckDSL` which can add this wrapper automatically. My implementation is not complete enough to see whether the non-injectivity of the `DSL` type family leads to road blocks that weren’t there with a GADT. Anyways, thanks a lot for giving so much thought to this problem of mine.
discourse.haskell.org
August 10, 2026 at 6:30 AM
Derived data family instance
That is essentially the approach I am testing currently. From the GHC.Generics module I extracted these key design points: 1. All constructors that you would normally put in a data family or GADT get their own data type, like `:+:` or `:*:`. 2. The helper class (`Generic` in that case) has an associated type family, which is not injective, but… 3. … still allows one to gain _some_ information about the source type by pattern matching on the constructors of the type family target. 4. Due to non-injectivity, all further derived instances (e.g. `Eq`) must work on the dedicated types from (1) 5. The _derived family instance_ can be implemented using the same mechanism that the `Generically` newtype is offering. This corresponds to your `MkTG` constructor. The only drawback is that this can pollute the type family target type with yet another newtype wrapper. AntC2: > I don’t pretend to understand what you’re trying to do My use case is a representation of an (externally specified) domain-specific language (DSL). There are more things to do with it, but the basic thing is evaluation: type Semantics DSL a = (a -> Bool) -- concrete semantics is irrelevant runDSL :: DSL a -> Semantics (DSL a) The naive approach would be to just encode the AST. newtype DSL a = DSLExpr {getAST :: AST} However, not all expressions might be valid for all types. Hence we must have typeCheckDSL :: AST -> Maybe (Semantics DSL a) If that sort of run-time failure is not desired, one would traditionally reach for a GADT. data DSL a where -- many GADT constructors typeCheckDSL :: AST -> Maybe (DSL a) runDSL :: DSL a -> (Semantics DSL a) -- now type safe Now the type mismatch can be thrown at parsing stage. However, the GADT is _closed_ while the basic library may not want to cover the DSL spec for _all Haskell types_. In contrast a type class is open and extensible. class RunDSL a where type DSL a typeCheckDSL :: AST -> Maybe (DSL a) runDSL :: DSL a -> (Semantics DSL a) My goal is now to provide instances for a wide range of types without much boilerplate. To that end, we provide re-usable `DSL a` building blocks as separate types, like the ones used in a Generic `Rep`. If we covered all `Rep` types in our DSL class, then we can have the desired derived instance: newtype GenericDSL a = GenericDSL (forall p. DSL (Rep a p)) If we now declare for a concrete type deriving via (Generically T) instance RunDSL T then the `DSL T` type would have the `GenericDSL` newtype wrapper as single constructor, witnessing the fact that we used a derived instance. But that is okay, since the main interface for construction is `typeCheckDSL` which can add this wrapper automatically. My implementation is not complete enough to see whether the non-injectivity of the `DSL` type family leads to road blocks that weren’t there with a GADT. Anyways, thanks a lot for giving so much thought to this problem of mine.
discourse.haskell.org
August 10, 2026 at 2:49 AM
Derived data family instance
That is essentially the approach I am testing currently. From the GHC.Generics module I extracted these key design points: 1. All constructors that you would normally put in a data family or GADT get their own data type, like `:+:` or `:*:`. 2. The helper class (`Generic` in that case) has an associated type family, which is not injective, but… 3. … still allows one to gain _some_ information about the source type by pattern matching on the constructors of the type family target. 4. Due to non-injectivity, all further derived instances (e.g. `Eq`) must work on the dedicated types from (1) 5. The _derived family instance_ can be implemented using the same mechanism that the `Generically` newtype is offering. This corresponds to your `MkTG` constructor. The only drawback is that this can pollute the type family target type with yet another newtype wrapper. AntC2: > I don’t pretend to understand what you’re trying to do My use case is a representation of an (externally specified) domain-specific language (DSL). There are more things to do with it, but the basic thing is evaluation: type Semantics DSL a = (a -> Bool) -- concrete semantics is irrelevant runDSL :: DSL a -> Semantics (DSL a) The naive approach would be to just encode the AST. newtype DSL a = DSLExpr {getAST :: AST} However, not all expressions might be valid for all types. Hence we must have typeCheckDSL :: AST -> Maybe (Semantics DSL a) If that sort of run-time failure is not desired, one would traditionally reach for a GADT. data DSL a where -- many GADT constructors typeCheckDSL :: AST -> Maybe (DSL a) runDSL :: DSL a -> (Semantics DSL a) -- now type safe Now the type mismatch can be thrown at parsing stage. However, the GADT is _closed_ while the basic library may not want to cover the DSL spec for _all Haskell types_. In contrast a type class is open and extensible. class RunDSL a where type DSL a typeCheckDSL :: AST -> Maybe (DSL a) runDSL :: DSL a -> (Semantics DSL a) My goal is now to provide instances for a wide range of types without much boilerplate. To that end, we provide re-usable `DSL a` building blocks as separate types, like the ones used in a Generic `Rep`. If we covered all `Rep` types in our DSL class, then we can have the desired derived instance: newtype GenericDSL a = GenericDSL (forall p. DSL (Rep a p)) If we now declare for a concrete type deriving via (Generically T) instance RunDSL T then the `DSL T` type would have the `GenericDSL` newtype wrapper as single constructor, witnessing the fact that we used a derived instance. But that is okay, since the main interface for construction is `typeCheckDSL` which can add this wrapper automatically. My implementation is not complete enough to see whether the non-injectivity of the `DSL` type family leads to road blocks that weren’t there with a GADT. Anyways, thanks a lot for giving so much thought to this problem of mine.
discourse.haskell.org
August 9, 2026 at 1:31 PM
Default/overlapping data family instance
Related question from 2023: Choosing data representation based on type As far as I understand, a GADT is like a closed data family. I want to construct an _open_ data family, where the library user can add her own instances for concrete types that weren’t mentioned in the library. The data family is associated with a type class. Thanks to `DefaultSignatures`, we can provide default implementations for ordinary **class methods** like so: class Blah x where blah :: x -> Blubb default blah :: Generic x => x -> Blubb blah = -- implementation using the context Likewise, I would like to create a default or overlappable data family instance, but it seems to be rejected for reasons explained in the thread linked to above. Assume we have a data family `MyOpenFamily` and we are able to use `MyOpenFamily x` and `MyOpenFamily (Rep x ())` interchangingly. The goal is to default class Blah x where blah :: MyOpenFamily x -> Blubb I would like to give the library user the same convenience as with `DefaultSignatures`. * No need to declare an instance of `MyOpenFamily` provided a `Generic` instance can be derived. * No need to declare an implementation for `blah` provided a `Generic` instance can be derived. Ugly and potentially circular work-around (related to the `Generically` trick): Further wrap the open data family in yet another GADT with two constructors, like so: data MyFamilyWrapper x where DirectInstance :: MyOpenFamily x -> MyFamilyWrapper x ViaGeneric :: (Generic x, f ~ Rep x) => MyOpenFamily (f ()) -> MyFamilyWrapper x Then change the type of `blah`: class Blah where blah :: MyFamilyWrapper x -> Blubb For non-Generic types, the user provides a definition of `blah` that pattern-matches only on `DirectInstance`. Thanks to the constraint on `ViaGeneric`, we know that is a total definition. For Generic types, we can then have a default implementation: default blah :: (Generic x, Rep x ~ f, Blah (f ())) => MyFamilyWrapper x -> Blubb blah (ViaGeneric p) = blah (DirectInstance p) I wonder whether there is a more concise way of doing this, without `MyFamilyWrapper`. Using ordinary type families is out of the question because * the real class involves kinds `Type -> Type` and type families can’t be partially applied, * a default clause `type MyOpenFamily a = MyOpenFamily (Rep a ())` in a type family makes it closed.
discourse.haskell.org
July 16, 2026 at 5:28 PM
Have you tried this pattern for tagless-initial-style types in Rust?
Zero-Cost 'Tagless Final' in Rust with GADT-style Enums
A deep dive into implementing the 'tagless initial' pattern in Rust using enums and the never type to achieve zero-cost abstractions, demonstrated with optimized assembly output.
inferara.com
July 9, 2026 at 3:27 PM
And then along comes someone who's fairly well-versed in what we do and decides to adopt exactly the same design as ours (that famous GADT for defining the devices our unikernel needs) without acknowledging the connection (I know this GADT; I spent a long time sitting in front of it!) […]
Original post on mastodon.social
mastodon.social
June 23, 2026 at 9:20 PM
Five unikernels later, we’ve confirmed (to some extent) that the #gadt we introduced two years ago was indeed a good idea. And that’s to be expected! I already told my manager at the time that it takes an average of 10 years to get a good API…
June 23, 2026 at 9:19 PM
This project had one unique feature: it allowed us to specify the devices needed by the unikernel using a #gadt. This GADT didn’t come out of nowhere; it was originally developed for another project called mimic, which was itself inspired by yet another project named crowbar. The ingenuity here […]
Original post on mastodon.social
mastodon.social
June 23, 2026 at 9:17 PM