{"id":18408880,"url":"https://github.com/lamdu/hypertypes","last_synced_at":"2025-04-08T03:14:51.444Z","repository":{"id":56844527,"uuid":"158718783","full_name":"lamdu/hypertypes","owner":"lamdu","description":"Hypertypes - generic programming for heterogeneous recursive types","archived":false,"fork":false,"pushed_at":"2024-09-09T09:30:13.000Z","size":1910,"stargazers_count":112,"open_issues_count":9,"forks_count":9,"subscribers_count":10,"default_branch":"main","last_synced_at":"2024-10-29T11:29:39.550Z","etag":null,"topics":["programming-language-development","programming-languages","type-checker","type-inference"],"latest_commit_sha":null,"homepage":"","language":"Haskell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lamdu.png","metadata":{"files":{"readme":"README.md","changelog":"ChangeLog.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-11-22T15:30:10.000Z","updated_at":"2024-09-17T16:13:06.000Z","dependencies_parsed_at":"2023-01-30T23:45:17.114Z","dependency_job_id":"e110f360-7e58-4039-a098-bb783bad9824","html_url":"https://github.com/lamdu/hypertypes","commit_stats":{"total_commits":1219,"total_committers":7,"mean_commits":"174.14285714285714","dds":0.08121410992616895,"last_synced_commit":"186aac2f32c775043175a95a0f195e013511637c"},"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lamdu%2Fhypertypes","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lamdu%2Fhypertypes/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lamdu%2Fhypertypes/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lamdu%2Fhypertypes/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lamdu","download_url":"https://codeload.github.com/lamdu/hypertypes/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247767236,"owners_count":20992548,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["programming-language-development","programming-languages","type-checker","type-inference"],"created_at":"2024-11-06T03:22:05.229Z","updated_at":"2025-04-08T03:14:51.400Z","avatar_url":"https://github.com/lamdu.png","language":"Haskell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# hypertypes: Types parameterised by hypertypes\n\nHypertypes enable constructing rich recursive types from individual components, and processing them generically with type classes.\n\nThey are a solution to the *Expression Problem*, as described by Phil Wadler (1998):\n\n\u003e The goal is to define a data type by cases, where one can add new cases to the data type and new functions over the data type, without recompiling existing code, and while retaining static type safety.\n\n[*Data types a la carte*](http://www.staff.science.uu.nl/~swier004/publications/2008-jfp.pdf) (DTALC, Swierstra, 2008) offers a solution for the expression problem which is only applicable for simple recursive expressions, without support for mutually recursive types. In practice, programming language ASTs do tend to be mutually recursive. [`multirec`](http://hackage.haskell.org/package/multirec) (Rodriguez et al, 2009) uses GADTs to encode mutually recursive types but in comparison to DTALC it lacks in the ability to construct the types from re-usable components.\n\nHypertypes allow constructing expressions from re-usable terms like DTALC, which can be rich mutually recursive types like in `multirec`.\n\nThe name \"Hypertypes\" is inspired by *Hyperfunctions* (S. Krstic et al, FICS 2001), which are a similar construct at the value level.\n\n## Introduction to the \"field constructor\" pattern\n\nTo motivate hypertypes and explain the expressive power they allow, this section will discuss the benefits and limitations of other forms of constructing data types, ranging from unparameterized types to more powerful approaches:\n\n* `Type` - unparameterized types\n* `Type -\u003e Type` - simple parameterized types\n* `(Type -\u003e Type) -\u003e Type` - also known as HKD (Higher-Kinded Data)\n* `(Index -\u003e Type) -\u003e Index -\u003e Type` - `multirec`'s approach\n\n### `Type`: Simple type, simple functionality\n\nSuppose we have the following type in an application:\n\n```Haskell\ndata Person = Person\n    { height :: Double\n    , weight :: Double\n    }\n```\n\nLet's imagine that we want to let a user fill in a `Person` via a form,\nwhere during the process the record may have missing fields.\n\nWe may want a way to represent a state with missing fields,\nbut this type doesn't allow for it.\n\nWe can either create an additional type for that, or augment `Person` to provide more functionality. Augmenting `Person` is preferred because it will result in less boiler-plate and less types to maintain as we make changes to it.\n\n### `Type -\u003e Type`: Adding a type parameter\n\nA possible solution is to parameterize `Person` on the field type:\n\n```Haskell\ndata Person a = Person\n    { height :: a\n    , weight :: a\n    }\n```\n\nThis would solve our problem.\n\nWe can parameterize with `Double` for the normal structure,\nand with `Maybe Double` for the variant with missing fields.\n\nThis approach reaches its limits when the fields have multiple different types, as in:\n\n```Haskell\ndata Person = Person\n    { height :: Double\n    , weight :: Double\n    , name :: Text\n    }\n```\n\nWe would now need an additional parameter to parameterize how to store the fields of type `Text`!\nIs there a way to use a single type parameter for both types of fields? Yes, there is:\n\n### `(Type -\u003e Type) -\u003e Type`: Higher-Kinded Data\n\nThe [\"Higher-Kinded Data\"](https://reasonablypolymorphic.com/blog/higher-kinded-data/) pattern represents `Person` like so:\n\n```Haskell\ndata Person f = Person\n    { height :: f Double\n    , weight :: f Double\n    , name :: f Text\n    }\n```\n\nFor the plain case we would use `Person Identity`.\n\n`Identity` from `Data.Functor.Identity` is defined as so:\n\n```Haskell\ndata Identity a = Identity a\n```\n\nAnd for the variant with missing fields we would use `Person Maybe`.\n\nThe benefit of this parameterization over the previous one is that `Person`'s kind\ndoesn't need to change when adding more field types, so such changes don't propagate all over the code base.\n\nNote that various helper classes such as\n`Rank2.Functor`\nand `Rank2.Traversable` (from the [`rank2classes`](https://hackage.haskell.org/package/rank2classes) package)\nallow us to conveniently convert between `Person Identity` and `Person Maybe`.\n\n#### HKD for nested structures\n\nLet's employ the same transformation we did for `Person` to a more complicated data structure:\n\n```Haskell\ndata Expr\n    = Const Int\n    | Add Expr Expr\n    | Mul Expr Expr\n```\n\nThe HKD form of `Expr` would be:\n\n```Haskell\ndata Expr f\n    = Const (f Int)\n    | Add (f (Expr f)) (f (Expr f))\n    | Mul (f (Expr f)) (f (Expr f))\n```\n\nThis does allow representing nested structures with missing elements.\nBut classes like `Rank2.Functor` no longer work for it.\nTo understand why let's look at `Rank2.Functor`'s definition\n\n```Haskell\nclass Functor f where\n    (\u003c$\u003e) :: (forall a. p a -\u003e q a) -\u003e f p -\u003e f q\n```\n\nThe rank-2 function argument expects the field type `a` to stay the same when it changes `p` to `q`,\nhowever in the above formulation of `Expr` the field type `Expr p` change to `Expr q` when changing the type parameter.\n\n### `Type -\u003e Type`: The DTALC and `recursion-schemes` approach\n\nAnother formulation of `Expr` is the same as the `Type -\u003e Type` approach discussed above:\n\n```Haskell\ndata Expr a\n    = Const Int\n    | Add a a\n    | Mul a a\n```\n\nNotes:\n\n* The [`recursion-schemes`](http://hackage.haskell.org/package/recursion-schemes) package can generate this type for us from the plain definition of `Expr` using `TemplateHaskell`\n* DTALC also allows us to construct this type by combining standalone `Const`, `Add`, and `Mul` types with the `:+:` operator (i.e `Const Int :+: Add :+: Mul`)\n\nThis approach does have the single node type limitation, so we gave up on parameterizing over the `Int` in `Const`.\nThis is a big limitation, but as we'll see, we do get several advantages in return.\n\nFirst, we can represent plain expressions as `Fix Expr`, using:\n\n```Haskell\nnewtype Fix f = Fix (f (Fix f))\n```\n\nWe can then use useful combinators from `recursion-schemes` for folding and processing of `Expr`s.\n\n[`unification-fd`](http://hackage.haskell.org/package/unification-fd)\nis a good example of the power of this approach.\nIt implements generic unification for ASTs,\nwhere it uses the parameterization to represent sub-expressions via unification variables.\n\nIn constrast to the HKD approach, we can also use rich fix-points which store several different fix-points within, like `Diff`:\n\n```Haskell\ndata Diff f\n    = Same (f (Fix f))\n    | SameTopLevel (f (Diff f))\n    | Different (f (Fix f)) (f (Fix f))\n```\n\n(Note how `Diff` parameterizes `f` by both `Fix` and `Diff`)\n\nThe main drawback of this approach is that in practice ASTs tend to be mutually recursive datatypes. For example:\n\n```Haskell\ndata Expr\n    = Var Text\n    | App Expr Expr\n    | Lam Text Typ Expr\ndata Typ\n    = IntT\n    | FuncT Typ Typ\n```\n\nThis type is an example for an AST which DTALC and `recursion-schemes` cannot represent.\n\nCan the \"field constructor\" pattern be used to represent such ASTs? Yes:\n\n### `(Index -\u003e Type) -\u003e Index -\u003e Type`: The `multirec` approach\n\n[`multirec`](http://hackage.haskell.org/package/multirec)'s way to define the above AST:\n\n```Haskell\ndata Expr :: Index\ndata Typ :: Index\n\ndata AST :: (Index -\u003e Type) -\u003e Index -\u003e Type where\n    Var :: Text -\u003e AST r Expr\n    App :: r Expr -\u003e r Expr -\u003e AST r Expr\n    Lam :: Text -\u003e r Typ -\u003e r Expr -\u003e AST r Expr\n    IntT :: AST r Typ\n    FuncT :: r Typ -\u003e r Typ -\u003e AST r Typ\n```\n\n(this is a slight variant of `multirec`'s actual presentation, where for improved legibility `Index` is used rather than `Type`)\n\n`multirec` offers various utilities to process such data types.\nIt offers [`HFunctor`](http://hackage.haskell.org/package/multirec-0.7.9/docs/Generics-MultiRec-HFunctor.html),\na variant of `Functor` for these structures, and various recursive combinators.\n\nBut `multirec` has several limitations:\n\n* Using a single GADT for the data type limits composition and modularity.\n* Invocations of `HFunctor` for a `Typ` node need to support transforming all indices of `AST`,\n  including `Expr`, even though `Typ` doesn't have `Expr` child nodes.\n\n## `hypertypes`'s approach\n\nThe `hypertypes` representation of the above AST example:\n\n```Haskell\ndata Expr h\n    = EVar Text\n    | EApp (h :# Expr) (h :# Expr)\n    | ELam Text (h :# Typ) (h :# Expr)\ndata Typ h\n    = TInt\n    | TFunc (h :# Typ) (h :# Typ)\n```\n\nSub-expressions are nested using the `:#` type operator. On the left side of `:#` is `Expr`'s type parameter `h` which is the \"nest type\", and on the right side `Expr` and `Typ` are the nested nodes.\n\n`:#` is defined as:\n\n```Haskell\n-- A type parameterized by a hypertype\ntype HyperType = AHyperType -\u003e Type\n\n-- A kind for hypertypes\nnewtype AHyperType = AHyperType { getHyperType :: HyperType }\n\n-- GetHyperType is getHyperType lifted to the type level\ntype family GetHyperType h where\n    GetHyperType ('AHyperType t) = t\n\ntype p :# q = (GetHyperType p) ('AHyperType q)\n-- AHyperType is DataKinds syntax for using AHyperType in types\n```\n\nThe `hypertypes` library provides:\n\n* Variants of standard classes like `Functor` for hypertypes with derivations.\n  (Unlike in `multirec`'s `HFunctor`, only the actual child node types of each node need to be handled)\n* Combinators for recursive processing and transformation of nested structures\n* Implementations of common AST terms\n* A unification implementation for mutually recursive types inspired by `unification-fd`\n* A generic and fast implementation of Hindley-Milner type inference (\"Efficient generalization with levels\" as described in [*How OCaml type checker works*](http://okmij.org/ftp/ML/generalization.html), Kiselyov, 2013)\n\n## Constructing types from individual components\n\nNote that another way to formulate the above expression would be using pre-existing parts, such as:\n\n```Haskell\ndata RExpr h\n    = RVar (Var Text RExpr h)\n    | RApp (App RExpr h)\n    | RLam (TypedLam Text Typ RExpr h)\n    deriving (Generic, Generic1, HNodes, HFunctor, HFoldable, HTraversable, ZipMatch)\n```\n\nThis form supports using `DeriveAnyClass` to derive instances for various `HyperType` classes such as `HFunctor` based on `Generic1`. Note that due to a technical limitation of `Generic1` the form of `Expr` from before, which directly nests values, doesn't have a `Generic1` instance (so the instances for `Expr` are derived using `TemplateHaskell` instead).\n\n## Examples\n\nHow do we represent an expression of the example language declared above?\n\nLet's start with the verbose way:\n\n```Haskell\nverboseExpr :: Pure # Expr\nverboseExpr =\n    Pure (ELam \"x\" (Pure TInt) (Pure (EVar \"x\")))\n```\n\nExplanations for the above:\n\n* `Pure # Expr` is a type synonym for `Pure ('AHyperType Expr)`\n* `Pure` is the simplest \"pass-through\" nest type\n* The above is quite verbose with a lot of instances of `Pure` and many parentheses\n* Writing an expression of the above `RExpr` would be even more verbose due to additional `Var` and `TypedLam` data constructors!\n\nTo write it more consicely, the `HasHPlain` class, along with a `TemplateHaskell` generator for it, exists:\n\n```Haskell\n\u003e let e = hPlain # verboseExpr\n-- Note: This (#) comes from Control.Lens\n\n\u003e e\nELamP \"x\" TIntP (EVarP \"x\")\n\n\u003e :t e\ne :: HPlain Expr\n```\n\nIt's now easier to see that `e` represents `λ(x:Int). x`\n\n`HPlain` is a data family of \"plain versions\" of expressions, generated via `TemplateHaskell`. Note that it flattens embedded constructors for maximal convinience, so that the plain version of `RExpr` is as convinient to use as that of `Expr`!\n\nThis is somewhat similar to how `recursion-schemes` can derive a parameterized version of an AST, but is the other way around: the parameterized type is the source and the plain one is generated.\n\nSo now, let's define some example expressions concisely:\n\n```Haskell\nexprA, exprB :: HPlain Expr\n\nexprA = ELamP \"x\" IntTP (EVarP \"x\")\n\nexprB = ELamP \"x\" (TFuncP TIntP TIntP) (EVarP \"x\")\n```\n\nWhat can we do with these expressions?\nLet's compute a diff:\n\n```Haskell\n\u003e let d = diffP exprA exprB\n\n\u003e d\nCommonBodyP\n(ELam \"x\"\n    (DifferentP TIntP (TFuncP TIntP TIntP))\n    (CommonSubTreeP (EVarP \"x\"))\n)\n\n\u003e :t d\nd :: DiffP # Expr\n-- (An Expr with the DiffP nest type)\n```\n\nLet's see the type of `diffP`:\n\n```Haskell\n\u003e :t diffP\ndiffP ::\n    ( RTraversable h\n    , Recursively ZipMatch h\n    , Recursively HasHPlain h\n    ) =\u003e\n    HPlain h -\u003e HPlain h -\u003e DiffP # h\n```\n\n`diffP` can compute the diff for any AST that is recursively traversable, can be matched, and has a plain representation.\n\nNow, let's format this diff better:\n\n```Haskell\n\u003e let formatDiff _ x y = \"- \" \u003c\u003e show x \u003c\u003e \"\\n+ \" \u003c\u003e show y \u003c\u003e \"\\n\"\n\n\u003e putStrLn (foldDiffsP formatDiff d)\n- TIntP\n+ TFuncP TIntP TIntP\n\n\u003e :t foldDiffsP\nfoldDiffsP ::\n    ( Monoid r\n    , Recursively HFoldable h\n    , Recursively HasHPlain h\n    ) =\u003e\n    (forall n. HasHPlain n =\u003e HRecWitness h n -\u003e HPlain n -\u003e HPlain n -\u003e r) -\u003e\n    DiffP # h -\u003e\n    r\n```\n\nWhy is the ignored argument of `formatDiff` there? It is the `HRecWitness h n` from the type of `foldDiffsP` above. It is a witness that \"proves\" that the folded node `n` is a recursive node of `h`, essentially restricting the `forall n.` to `n`s that are recursive nodes of `h`.\n\n## Witness parameters\n\n*First, I want to give thanks and credit: We learned of this elegant solution from `multirec`!*\n\nWhat are witness parameters?\n\nLet's look at how `HFunctor` is defined:\n\n```Haskell\nclass HNodes h =\u003e HFunctor h where\n    -- | 'HFunctor' variant of 'fmap'\n    hmap ::\n        (forall n. HWitness h n -\u003e p # n -\u003e q # n) -\u003e\n        h # p -\u003e\n        h # q\n```\n\n`HFunctor` can change an `h`'s nest-type from `p` to `q`.\n\n`HWitness` is a data family which is a member of `HNodes`.\n\nFor example, let's see the definition of `Expr`'s `HWitness`:\n\n```Haskell\ndata instance HWitness Expr n where\n    W_Expr_Expr :: HWitness Expr Expr\n    W_Expr_Typ :: HWitness Expr Typ\n```\n\nNote that this GADT is automatically generated via `TemplateHaskell`.\n\nWhat does the witness give us? It restricts `forall n.` to the nodes of `h`.\nWhen mapping over an `Expr` we can:\n\n* Ignore the witness and use a mapping from a `p` of any `n` to a `q` of it\n* Pattern match on the witness to handle `Expr`'s specific node types\n* Use the `#\u003e` operator to convert the witness to a class constraint on `n`.\n\n## Understanding `HyperType`s\n\n* We want structures to be parameterized by nest-types\n* Nest-types are parameterized by the structures, too\n* Therefore, structures and their nest-types need to be parameterized by each other\n* This results in infinite types, as the structure is parameterized by something which may be parameterized by the structure itself.\n\n`multirec` ties this knot by using indices to represent types. `hypertypes` does this by using `DataKinds` and the `AHyperType` `newtype` which is used for both structures and their nest-types. An implication of the two being the same is that the same classes and combinators are re-used for both.\n\n## What Haskell is this\n\n`hypertypes` is implemented with GHC and heavily relies on quite a few language extensions:\n\n* `ConstraintKinds` and `TypeFamilies` are needed for the `HNodesConstraint` type family that lifts a constraint to apply over a value's nodes. Type families are also used to encode term's results in type inference.\n* `DataKinds` allows parameterizing types over `AHyperType`s\n* `DefaultSignatures` are used for default methods that return `Dict`s to avoid undecidable super-classes\n* `DeriveGeneric`, `DerivingVia`, `GeneralizedNewtypeDeriving`, `StandaloneDeriving` and `TemplateHaskell` are used to derive type-class instances\n* `EmptyCase` is needed for instances of leaf nodes\n* `FlexibleContexts`, `FlexibleInstances` and `UndecidableInstances` are required to specify many constraints\n* `GADTs` and `RankNTypes` enable functions like `hmap` which get `forall`ed functions with witness parameters\n* `MultiParamTypeClasses` is needed for the `Unify` and `Infer` type classes\n* `ScopedTypeVariables` and `TypeApplications` assist writing short code that type checks\n\nMany harmless syntactic extensions are also used:\n\n* `DerivingStrategies`, `LambdaCase`, `TupleSections`, `TypeOperators`\n\n## How does hypertypes compare/relate to\n\nNote that comparisons to `multirec`, HKD, `recursion-schemes`, `rank2classes`, and `unification-fd` were discussed above.\n\nIn addition:\n\n### hyperfunctions\n\nS. Krstic et al [KLP2001] have described the a type which they call a \"Hyperfunction\". Here is it's definition from the [`hyperfunctions`](http://hackage.haskell.org/package/hyperfunctions) package:\n\n```Haskell\nnewtype Hyper a b = Hyper { invoke :: Hyper b a -\u003e b }\n```\n\n`AHyperType`s are isomorphic to `Hyper Type Type` (assuming a `PolyKinds` variant of `Hyper`), so they can be seen as type-level \"hyperfunctions\".\n\nFor more info on hyperfunctions and their use cases in the value level see [LKS2013]\n\n#### References\n\n* [KLP2001] S. Krstic, J. Launchbury, and D. Pavlovic. Hyperfunctions. In Proceeding of Fixed Points in Computer Science, FICS 2001\n* [LKS2013] J. Launchbury, S. Krstic, T. E. Sauerwein. [Coroutining Folds with Hyperfunctions](https://arxiv.org/abs/1309.5135). In In Proceedings Festschrift for Dave Schmidt, EPTCS 2013\n\n### Data Types a la Carte\n\nIn addition to the external fix-points described above, [Data Types a la Carte](http://www.staff.science.uu.nl/~swier004/publications/2008-jfp.pdf) (DTALC) also describes how to define ASTs structurally.\n\nI.e, rather than having\n\n```Haskell\ndata Expr a\n    = Val Int\n    | Add a a -- \"a\" stands for a sub-expression (recursion-schemes style)\n```\n\nWe can have\n\n```Haskell\nnewtype Val a = Val Int\n\ndata Add a = Add a a\n\n-- Expr is a structural sum of Val and Add\ntype Expr = Val :+: Add\n```\n\nThis enables re-usability of the AST elements `Val` and `Add` in various ASTs, where the functionality is shared via type classes. Code using these type classes can work generically for different ASTs.\n\nLike DTALC, `hypertypes` has:\n\n* Instances type for combinators such as `:+:` and `:*:`, so that these can be used to build ASTs\n* Implementations of common AST terms in the `Hyper.Type.AST` module hierarchy (`App`, `Lam`, `Let`, `Var`, `TypeSig` and others)\n* Classes like `HFunctor`, `HTraversable`, `Unify`, `Infer` with instances for the provided AST terms\n\nAs an example of a reusable term let's look at the definition of `App`:\n\n```Haskell\n-- | A term for function applications.\ndata App expr h = App\n    { _appFunc :: h :# expr\n    , _appArg :: h :# expr\n    }\n```\n\nUnlike a DTALC-based apply, which would be parameterized by a single type parameter `(a :: Type)`, `App` is parameterized on two type parameters, `(expr :: HyperType)` and `(h :: AHyperType)`. `expr` represents the node type of `App expr`'s child nodes and `h` is the tree's fix-point. This enables using `App` in mutually recursive ASTs where it may be parameterized by several different `expr`s.\n\nUnlike DTALC, in `hypertypes` one typically needs to explicitly declare the datatypes for their expression types so that they can be used as `App`'s `expr` type parameter. Similarly, `multirec`'s DTALC variant also requires explicitly declaring type indices.\n\nWhile it is possible to declare ASTs as `newtype`s wrapping `:+:`s of existing terms and deriving all the instances via `GeneralizedNewtypeDeriving`, our usage and examples declare types in the straight forward way, with named data constructors, as we think that this results in more readable and performant code.\n\n### bound\n\n[`bound`](http://hackage.haskell.org/package/bound) is a library for expressing ASTs with type-safe De-Bruijn indices rather than parameter names, via an AST type constructor that is indexed on the variables in scope.\n\nAn intereseting aspect of `bound`'s ASTs is that recursively they are made of an infinite amount of types.\n\nWhen implementing `hypertypes` we had the explicit goal of making sure that such ASTs are expressible with it,\nand for this reason the `Hyper.Type.AST.NamelessScope` module in the tests implementing it is provided, and the test suite includes\na language implementation based on it (`LangA` in the tests).\n\n### lens\n\n`hypertypes` strives to be maximally compatible with [`lens`](http://hackage.haskell.org/package/lens), and offers `Traversal`s and `Setter`s wherever possible. But unfortunately the `RankNTypes` nature of many combinators in hypertypes makes them not composable with optics. For the special simpler cases when all child nodes have the same types the `htraverse1` traversal and `hmapped1` setter are available.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flamdu%2Fhypertypes","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flamdu%2Fhypertypes","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flamdu%2Fhypertypes/lists"}