{"id":39811410,"url":"https://github.com/danidiaz/red-black-record","last_synced_at":"2026-01-18T12:44:35.513Z","repository":{"id":144921128,"uuid":"163597930","full_name":"danidiaz/red-black-record","owner":"danidiaz","description":"Extensible records and variants indexed by a type-level Red-Black tree.","archived":false,"fork":false,"pushed_at":"2024-04-27T17:50:14.000Z","size":381,"stargazers_count":18,"open_issues_count":11,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-04-28T14:32:44.540Z","etag":null,"topics":["haskell","higher-kinded","type-level-programming"],"latest_commit_sha":null,"homepage":"","language":"Haskell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/danidiaz.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-12-30T15:35:19.000Z","updated_at":"2024-04-27T17:42:00.000Z","dependencies_parsed_at":"2023-04-22T14:07:43.608Z","dependency_job_id":null,"html_url":"https://github.com/danidiaz/red-black-record","commit_stats":null,"previous_names":[],"tags_count":19,"template":false,"template_full_name":null,"purl":"pkg:github/danidiaz/red-black-record","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danidiaz%2Fred-black-record","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danidiaz%2Fred-black-record/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danidiaz%2Fred-black-record/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danidiaz%2Fred-black-record/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/danidiaz","download_url":"https://codeload.github.com/danidiaz/red-black-record/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danidiaz%2Fred-black-record/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28536012,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-18T10:13:46.436Z","status":"ssl_error","status_checked_at":"2026-01-18T10:13:11.045Z","response_time":98,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["haskell","higher-kinded","type-level-programming"],"created_at":"2026-01-18T12:44:35.418Z","updated_at":"2026-01-18T12:44:35.494Z","avatar_url":"https://github.com/danidiaz.png","language":"Haskell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# red-black-record\n\n## What's this?\n\nA library that provides extensible records and variants, both indexed by a\ntype-level [red-black](https://en.wikipedia.org/wiki/Red%E2%80%93black_tree)\ntree that maps `Symbol` keys to value types of any kind. The keys correspond to\nfield names in records, and to branch names in variants. Many record functions\nhave their variant mirror-images and viceversa.\n\nAt the term level, value types come wrapped in a type constructor of kind `q -\u003e\nType`, where `q` is the kind of value types. Typically, the type constructor\nwill be an [identity\nfunctor](http://hackage.haskell.org/package/sop-core-0.4.0.0/docs/Data-SOP.html#t:I),\nbut it can also be `Maybe` or some other `Applicative` for parsing, validation\nand so on.\n\nIf we forget about the keys and only keep the values, records are isomorphic to\n[n-ary unlabeled\nproducts](http://hackage.haskell.org/package/sop-core-0.4.0.0/docs/Data-SOP.html#t:NP),\nand variants are isomorphic to [n-ary unlabeled\nsums](http://hackage.haskell.org/package/sop-core-0.4.0.0/docs/Data-SOP.html#t:NS).\nThe [sop-core](http://hackage.haskell.org/package/sop-core) library provides\nsuch unlabeled types, along with a rich API for manipulating them.\nred-black-record defines conversion functions to facilitate working in the\n\"unlabeled\" world, if needed, and then coming back to records and variants.\n\nThere is another world towards which bridges must be built: the everyday\nHaskell world of conventional records and sums. In fact, one of the motivations\nof extensible records and variants is to serve as \"generalized\" versions of\nvanilla data types. Advanced use cases can rely on these generalized versions,\nthereby avoiding intrusive changes to the original types. red-black-record\nprovides conversion typeclasses with default implementations by way of\n[`GHC.Generic`](http://hackage.haskell.org/package/base-4.12.0.0/docs/GHC-Generics.html).\n\nFor examples on how to use the library, check the haddocks for the\n`Data.RBR.Examples` module.\n\n## FAQ\n\n### What extensions do I need to use this library?\n\n* `DataKinds`.\n\n* `TypeApplications` to be able to specify field and branch names.\n\n* `TypeFamilies`.\n\n* `FlexibleContexts`.\n\n* `DeriveGeneric` for interfacing with normal records.\n\n* `PartialTypeSignatures` for hiding complex tree types.\n\n### My type signatures are getting big and scary because of those type-level trees. What to do?\n\nThe\n[`-XPartialTypeSignatures`](https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html?#extension-PartialTypeSignatures)\nextension can help with that, in combination with the\n[-Wno-partial-type-signatures](https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wpartial-type-signatures)\nGHC flag that disables the warning message emitted when the underscore is\nencountered in a signature.\n\nThe flag can be set globally in the\n[ghc-options](https://www.haskell.org/cabal/users-guide/developing-packages.html?#pkg-field-ghc-options)\nsection of the .cabal file, and also for particular modules with the\n[OPTIONS_GHC](https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html?highlight=options_ghc#options-ghc-pragma)\nfile-header pragma.\n\n### The `Show` instance for record doesn't show any field names.\n\nThe field names exist only at the type level. Also, the `Show` instance uses\nn-ary products and sums from\n[sop-core](http://hackage.haskell.org/package/sop-core), which do not have\nfield labels.\n\nFor fancier output, use the \"pretty-show\" functions instead.\n\n### Working with two records, I'm getting errors about incompatible types even as both records have the exact same fields.\n\nAlas, the order of insertion in the type-level tree matters :( Different\ninsertion orders can produce structurally different trees, even as they encode\nthe same symbol-to-type map.\n\nAs a workaround, one can use the `-Subset` functions to convert between\nequivalent structures.\n\n### I can't insert into a record when a field with the same name but different type already exists. Why not simply overwrite it?\n\nThat limitation was intentional, because allowing it would make impossible to\nimplement of `widen` for `Variant`. One solution is to explicitly delete the\nfield and then insert it again.\n\n### The library doesn't use Proxy and relies on type application instead. But what’s the order of the type parameters?\n\nFor standalone functions, it’s the order in which the type variables appear in\nthe `forall`.\n\n### What's the deal with all those -I suffixed versions of functions?\n\nThis library aims to provide\n[HKD](http://reasonablypolymorphic.com/blog/higher-kinded-data/)-like\nfunctionality by wrapping all the fields of a record in a type constructor.\n\nBut sometimes we are working with \"pure\" records without effects, and we just\nwant to get and set a field's value. In that case, the type constructor that\nwraps each field will be an identity functor `I` (from\n[sop-core](http://hackage.haskell.org/package/sop-core)). The -I suffixed\nfunctions wrap and unwrap the field's value on behalf of the user.\n\n### What's the deal with all those -Subset suffixed versions of functions?\n\nThese functions target multiple fields or branches at the same time. They can\nbe used to build lawful lenses and prisms over fragmenst of a structure.\n\nThey can also be used to convert between type-level trees that have the same\nentries but different structure.\n\n### What about compilation times?\n\nCompilation times balloon for large records. In the tests folder there's\nan example (not run by default in the tests) of the construction of a 50-field\nrecord whose fields are afterwards accessed one by one. It takes about 22\nseconds to compile in my machine. \n\nCode involving deletion of fields and branches (like using the `winnow`\nfunction for `Variant`s) is currently poorly optimized and will compile\n[slower](https://github.com/danidiaz/red-black-record/issues/12) than that.\n\nThe default generics-based implementations of `FromRecord` and `FromVariant`\nuse the same type-level machinery as the getters and its use will likely slow\ndown compilation as well.\n\n## Inspirations\n\n* The code for the red-black tree has been lifted from Stefan Kahrs's code\n  [available\n  here](https://www.cs.kent.ac.uk/people/staff/smk/redblack/rb.html). See also\n  [this post](https://www.cs.kent.ac.uk/people/staff/smk/redblack/rb.html).\n\n* Besides depending on sop-core, I have copied and adapted code from it. In\n  particular the `KeysValuesAll` typeclass is a version of the `All` typeclass\n  from sop-core. \n\n* [Surgery for data\n  types](https://blog.poisson.chat/posts/2018-11-26-type-surgery.html).\n  [reddit](https://www.reddit.com/r/haskell/comments/a0gi4z/surgery_for_data_types/).\n\n## Alternatives\n\n* [generics-sop](http://hackage.haskell.org/package/generics-sop) and\n  [records-sop](http://hackage.haskell.org/package/records-sop). Like\n  red-black-record, both of these libraries build upon sop-core. They are in\n  fact written by the same author of sop-core. generics-sop can provide\n  sum-of-products representations of any datatype with a Generic instance\n  (red-black-record is more limited, it only converts types that fit the named\n  record or variant mold—so no types with anonymous fields for example). \n  \n  If you don't need to explicitly target *individual* fields in the generic\n  representation, you'll be better off using generics-sop instead of\n  red-black-record. \n  \n  On top of generics-sop, records-sop provides named field accessors and record\n  subtyping based on a type-level list of fields (unlike the type-level tree\n  used by red-black-record). It doesn't seem to provide variants.\n\n* [superrecord](http://hackage.haskell.org/package/superrecord). This library\n  provides very efficient field access at runtime because the fields are backed\n  internally by an array. Uses a *sorted* type-level list of fields, to avoid\n  the problems of multiple orderings of the same fields.\n\n* [vinyl](http://hackage.haskell.org/package/vinyl). One of the oldest and more\n  fully-featured extensible records libraries. Uses a type level list of\n  fields. The fields' values are wrapped in a type constructor, like in\n  sop-core. The records seem to use an auxiliary sum type that serves as a\n  \"code\" for the fields. See also\n  [vinyl-genercics](https://hackage.haskell.org/package/vinyl-generics).\n\n* [HTree](https://github.com/i-am-tom/learn-me-a-haskell#htree). Another\n  implementation of extensible records using type-level red-black trees.\n\n* [megarecord](https://github.com/jvanbruegge/Megarecord). Seems to be a\n  proof-of-concept for a future [row polymorphism\n  extension](https://github.com/ghc-proposals/ghc-proposals/pull/180) for\n  Haskell.\n\n* [generic-data-surgery](https://hackage.haskell.org/package/generic-data-surgery).\n  Lots of useful machinery for manipulating generic representations of\n  dataytpes, without requiring intrusive changes to the original\n  representation.\n\n* [Coxswain](https://ghc.haskell.org/trac/ghc/wiki/Plugins/TypeChecker/RowTypes/Coxswain).\n\n* [higgledy](http://hackage.haskell.org/package/higgledy). Provides you with HKD\n  versions of normal records.\n\n* [Flay](https://hackage.haskell.org/package/flay-0.4) Work generically on your datatype without knowing its shape nor its contents.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanidiaz%2Fred-black-record","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdanidiaz%2Fred-black-record","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanidiaz%2Fred-black-record/lists"}