{"id":16795651,"url":"https://github.com/lambdageek/unbound-generics","last_synced_at":"2025-04-10T02:21:38.895Z","repository":{"id":19294820,"uuid":"22532074","full_name":"lambdageek/unbound-generics","owner":"lambdageek","description":"Specify variable binding in syntax trees using GHC.Generics (reimplementation of Unbound)","archived":false,"fork":false,"pushed_at":"2024-03-21T00:42:21.000Z","size":305,"stargazers_count":56,"open_issues_count":18,"forks_count":18,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-03-31T18:08:12.037Z","etag":null,"topics":["bound","ghc-generics","haskell-library","substitution","unbound","variable-binding"],"latest_commit_sha":null,"homepage":"https://hackage.haskell.org/package/unbound-generics/","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/lambdageek.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":"2014-08-01T22:47:06.000Z","updated_at":"2024-10-21T19:33:04.000Z","dependencies_parsed_at":"2024-03-21T00:30:10.277Z","dependency_job_id":"2e124614-4481-408f-ad57-297e97b3e155","html_url":"https://github.com/lambdageek/unbound-generics","commit_stats":{"total_commits":170,"total_committers":15,"mean_commits":"11.333333333333334","dds":0.2705882352941177,"last_synced_commit":"37cf1db31a2e0a24bea755d0da55f8d468b3a9e3"},"previous_names":[],"tags_count":18,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lambdageek%2Funbound-generics","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lambdageek%2Funbound-generics/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lambdageek%2Funbound-generics/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lambdageek%2Funbound-generics/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lambdageek","download_url":"https://codeload.github.com/lambdageek/unbound-generics/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248143120,"owners_count":21054709,"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":["bound","ghc-generics","haskell-library","substitution","unbound","variable-binding"],"created_at":"2024-10-13T09:16:59.376Z","updated_at":"2025-04-10T02:21:38.858Z","avatar_url":"https://github.com/lambdageek.png","language":"Haskell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# unbound-generics\n\n[![Join the chat at https://gitter.im/lambdageek/unbound-generics](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/lambdageek/unbound-generics?utm_source=badge\u0026utm_medium=badge\u0026utm_campaign=pr-badge\u0026utm_content=badge)\n[![Discord](https://img.shields.io/discord/732650960471720076?logo=discord)](https://discord.gg/CRfu93W)\n\n[![Hackage](https://img.shields.io/hackage/v/unbound-generics.svg)](https://hackage.haskell.org/package/unbound-generics)\n[![CI](https://github.com/lambdageek/unbound-generics/workflows/CI/badge.svg)](https://github.com/lambdageek/unbound-generics/actions?query=workflow%3ACI+branch%3Amain)\n\u003c!-- [![Build Status](https://travis-ci.org/lambdageek/unbound-generics.svg)](https://travis-ci.org/lambdageek/unbound-generics) --\u003e\n\nSupport for programming with names and binders using GHC Generics.\n\n## Summary\n\nSpecify the binding structure of your data type with an expressive set of type combinators, and `unbound-generics`\nhandles the rest!  Automatically derives alpha-equivalence, free variable calculation, capture-avoiding substitution, and more. See [`Unbound.Generics.LocallyNameless`](src/Unbound/Generics/LocallyNameless.hs) to get started.\n\nThis is a reimplementation of (parts of) [unbound](http://hackage.haskell.org/package/unbound) but using [GHC generics](http://www.haskell.org/ghc/docs/latest/html/libraries/base-4.7.0.1/GHC-Generics.html) instead of [RepLib](https://hackage.haskell.org/package/RepLib).\n\n## Examples\n\nSome examples are in the `examples/` directory in the source.  And also at [unbound-generics on GitHub Pages](https://lambdageek.github.io/unbound-generics)\n\n### Example: Untyped lambda calculus interpreter\nHere is how you would implement call by value evaluation for the untyped lambda calculus:\n\n```haskell\n{-# LANGUAGE DeriveDataTypeable, DeriveGeneric, MultiParamTypeClasses #-}\nmodule UntypedLambdaCalc where\nimport Unbound.Generics.LocallyNameless\nimport GHC.Generics (Generic)\nimport Data.Typeable (Typeable)\n\n-- | Variables stand for expressions\ntype Var = Name Expr\n\n-- | Expressions\ndata Expr = V Var                -- ^ variables\n          | Lam (Bind Var Expr) -- ^ lambdas bind a variable within a body expression\n          | App Expr Expr       -- ^ application\n          deriving (Show, Generic, Typeable)\n\n-- Automatically construct alpha equivalence, free variable computation and binding operations.\ninstance Alpha Expr\n\n-- semi-automatically implement capture avoiding substitution of expressions for expressions\ninstance Subst Expr Expr where\n  -- `isvar` identifies the variable case in your AST.\n  isvar (V x) = Just (SubstName x)\n  isvar _     = Nothing\n\n-- evaluation takes an expression and returns a value while using a source of fresh names\neval :: Expr -\u003e FreshM Expr\neval (V x) = error $ \"unbound variable \" ++ show x\neval e@Lam{} = return e\neval (App e1 e2) = do\n  v1 \u003c- eval e1\n  v2 \u003c- eval e2\n  case v1 of\n   Lam bnd -\u003e do\n     -- open the lambda by picking a fresh name for the bound variable x in body\n     (x, body) \u003c- unbind bnd\n     let body' = subst x v2 body\n     eval body'\n   _ -\u003e error \"application of non-lambda\"\n\nexample :: Expr\nexample =\n  let x = s2n \"x\"\n      y = s2n \"y\"\n      e = Lam $ bind x (Lam $ bind y (App (V y) (V x)))\n  in runFreshM $ eval (App (App e e) e)\n  \n-- \u003e\u003e\u003e example\n-- Lam (\u003cy\u003e App (V 0@0) (Lam (\u003cx\u003e Lam (\u003cy\u003e App (V 0@0) (V 1@0)))))\n\n```\n## Differences from `unbound`\n\nFor the most part, I tried to keep the same methods with the same signatures.  However there are a few differences.\n\n1. `fv :: Alpha t =\u003e Fold t (Name n)`\n\n   The `fv` method returns a `Fold` (in the sense of the [lens](http://hackage.haskell.org/package/lens) library),\n   rather than an `Unbound.Util.Collection` instance.  That means you will generally have to write `toListOf fv t` or some    other summary operation.\n\n2. Utility methods in the `Alpha` class have different types.\n\n   You should only notice this if you're implementing an instance of `Alpha` by hand (rather than by using the default\n   generic instance).\n   \n   1. `isPat :: Alpha t =\u003e t -\u003e DisjointSet AnyName`\n     The original `unbound` returned a `Maybe [AnyName]` here with the same interpretation as `DisjointSet`: `Nothing` means an inconsistency was encountered, or `Just` the free variables of the pattern.\n   2. `isTerm :: Alpha t =\u003e t -\u003e All`\n   3. `open :: Alpha t =\u003e AlphaCtx -\u003e NthPatFind -\u003e t -\u003e t`, `close :: Alpha t =\u003e AlphaCtx -\u003e NamePatFind -\u003e t -\u003e t` where `NthPatFind` and `NamePatFind` are newtypes\n\n3. `embed :: IsEmbed e =\u003e Embedded e -\u003e e` and `unembed :: IsEmbed e =\u003e e -\u003e Embedded e`\n\n    The typeclass `IsEmbed` has an `Iso` (again in the sense of the `lens` library) as a method instead of the above pair of methods.\n\n    Again, you should only notice this if you're implementing your own types that are instances of `IsEmbed`.  The easiest thing to do is to use implement `embedded = iso yourEmbed yourUnembed` where `iso` comes from `lens`.  (Although you can also implement it in terms of `dimap` if you don't want to depend on lens)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flambdageek%2Funbound-generics","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flambdageek%2Funbound-generics","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flambdageek%2Funbound-generics/lists"}