{"id":31942110,"url":"https://github.com/effectfully/stlc","last_synced_at":"2026-02-19T15:01:56.767Z","repository":{"id":31634849,"uuid":"35200036","full_name":"effectfully/STLC","owner":"effectfully","description":"Dependently typed Algorithm M and friends","archived":false,"fork":false,"pushed_at":"2018-06-02T12:47:01.000Z","size":2643,"stargazers_count":25,"open_issues_count":1,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-10-14T09:26:03.480Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Agda","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/effectfully.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-05-07T05:13:09.000Z","updated_at":"2023-06-16T07:33:12.000Z","dependencies_parsed_at":"2022-09-10T06:52:01.484Z","dependency_job_id":null,"html_url":"https://github.com/effectfully/STLC","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/effectfully/STLC","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/effectfully%2FSTLC","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/effectfully%2FSTLC/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/effectfully%2FSTLC/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/effectfully%2FSTLC/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/effectfully","download_url":"https://codeload.github.com/effectfully/STLC/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/effectfully%2FSTLC/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29619123,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-19T13:04:20.082Z","status":"ssl_error","status_checked_at":"2026-02-19T13:03:33.775Z","response_time":117,"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":[],"created_at":"2025-10-14T09:25:58.911Z","updated_at":"2026-02-19T15:01:56.739Z","avatar_url":"https://github.com/effectfully.png","language":"Agda","funding_links":[],"categories":[],"sub_categories":[],"readme":"# STLC-in-Agda\n\n## A quick taste\n\nGetting a universe polymorphic Agda function from a pure lambda term:\n\n```\nphoenix : Syntax⁽⁾\nphoenix = 4 # λ a b c d → a · (b · d) · (c · d)\n\nphoenixᵗ : Term ((b ⇒ c ⇒ d) ⇒ (a ⇒ b) ⇒ (a ⇒ c) ⇒ a ⇒ d)\nphoenixᵗ = term⁻ phoenix\n\nliftM2 : ∀ {α β γ δ} {A : Set α} {B : Set β} {C : Set γ} {D : Set δ}\n       -\u003e ((B -\u003e C -\u003e D) -\u003e (A -\u003e B) -\u003e (A -\u003e C) -\u003e A -\u003e D)\nliftM2 = eval phoenixᵗ\n```\n\n`C-c C-n liftM2` gives `λ {.α} {.β} {.γ} {.δ} {.A} {.B} {.C} {.D} x x₁ x₂ x₃ →\n  x (x₁ x₃) (x₂ x₃)`.\n\n## Overview\n\nThis is simply typed lambda calculus with type variables in Agda. We have raw [Syntax](src/STLC/Term/Syntax.agda):\n\n```\ndata Syntax n : Set where\n  var : Fin n -\u003e Syntax n\n  ƛ_  : Syntax (suc n) -\u003e Syntax n\n  _·_ : Syntax n -\u003e Syntax n -\u003e Syntax n\n```\n\n[typed terms](src/STLC/Term/Core.agda):\n\n```\ndata _⊢_ {n} Γ : Type n -\u003e Set where\n  var : ∀ {σ}   -\u003e σ ∈ Γ     -\u003e Γ ⊢ σ\n  ƛ_  : ∀ {σ τ} -\u003e Γ ▻ σ ⊢ τ -\u003e Γ ⊢ σ ⇒ τ\n  _·_ : ∀ {σ τ} -\u003e Γ ⊢ σ ⇒ τ -\u003e Γ ⊢ σ     -\u003e Γ ⊢ τ\n```\n\nand [a mapping](src/STLC/M/Main.agda) from the former to the latter via a type-safe version of algorithm M:\n\n```\nM : ∀ {n l} -\u003e (Γ : Con n l) -\u003e Syntax l -\u003e (σ : Type n)\n  -\u003e Maybe (∃ λ m -\u003e ∃ λ (Ψ : Subst n m) -\u003e mapᶜ (apply Ψ) Γ ⊢ apply Ψ σ)\n```\n\n`M` receives a context, a term and a type, and checks, whether there is a substitution that allows to typify the term in this context and with this type, after the substitution is applied to them. `M` uses rewrite rules under the hood — this simplifies the definition a lot.\n\nThere is an [NbE](src/STLC/NbE/Main.agda), which uses the traversal from [5].\n\nThere is [a part](src/STLC/NbE/Read.agda) of the liftable terms approach to NbE (described in [4]), which is used to coerce Agda's lambda terms to their first-order counterparts.\n\nThere is [a universe polymorphic eval](src/STLC/Semantics/Eval.agda).\n\nUsing all this we can, for example, make an Agda lambda term universe polymorphic:\n\n```\nmono-app : {A B : Set} -\u003e (A -\u003e B) -\u003e A -\u003e B\nmono-app f x = f x\n\npoly-app : ∀ {α β} {A : Set α} {B : Set β} -\u003e (A -\u003e B) -\u003e A -\u003e B\npoly-app = eval (read (inst 2 λ A B -\u003e mono-app {A} {B}))\n```\n\n## Details\n\nI turned off the eta rule for products, because Agda lacks sharing. My Agda is out of date a bit, so I redefined `Σ` as a `data` instead of placing `{-# NO_ETA Σ #-}` somewhere. Algorithm M is still incredibly slow, and the current `_\u003e\u003e=ᵀ_` eats twice the memory comparing to the previous version, which was more performant, but less usable and inference-friendly, which is important because types signatures are quite verbose.\n\nThe [Main](src/STLC/Main.agda) module contains\n\n```\non-typed : ∀ {α} {A : ∀ {n} {σ : Type n} -\u003e Term⁽⁾ σ -\u003e Set α}\n         -\u003e (f : ∀ {n} {σ : Type n} -\u003e (t : Term⁽⁾ σ) -\u003e A t) -\u003e ∀ e -\u003e _\non-typed f e = fromJustᵗ $ infer e \u003e\u003e=ᵗ f ∘ thicken ∘ core ∘ proj₂ ∘ proj₂\n\ntyped = on-typed $ id\nterm  = on-typed $ λ t {m Δ}   -\u003e generalize {m} Δ t\nterm⁻ = on-typed $ λ {n} t {Δ} -\u003e generalize {n} Δ t\nnormᵖ = on-typed $ pure ∘ erase ∘ norm\n```\n\n`on-typed` receives a function `f` and a term, tries to typify the term, makes type variables consecutive and strengthened, applies `f` to the result and removes `just`, when inferring is successful, or returns `lift tt` otherwise.\n\n`_\u003e\u003e=ᵗ_` constructs values of type `mx \u003e\u003e=ᵀ B`, which are \"either `nothing` or `B x`\". The idea is described [here](http://stackoverflow.com/questions/31105947/eliminating-a-maybe-at-the-type-level) (I changed the definition a bit though).\n\n`thicken` is defined in terms of `enumerate` described [here](http://stackoverflow.com/questions/33345899/how-to-enumerate-the-elements-of-a-list-by-fins-in-linear-time).\n\n`generalize` substitutes type variables with universally quantified types with universally quantified upper bounds for variables and prepends a universally quantified context:\n\n```\napp : ε {2} ⊢ (Var zero ⇒ Var (suc zero)) ⇒ Var zero ⇒ Var (suc zero)\napp = ƛ ƛ var (vs vz) · var vz\n\ngapp : ∀ {n σ τ} {Γ : Con n} -\u003e Γ ⊢ (σ ⇒ τ) ⇒ σ ⇒ τ\ngapp = generalize _ app\n```\n\n`normᵖ` normalizes a pure lambda term whenever it's typeable. Uses NbE under the hood.\n\n## Unsafeties\n\nTwo simple lemmas are used for for rewriting via the `REWRITE` pragma:\n\n```agda\napply-apply : ∀ {n m p} {Φ : Subst m p} {Ψ : Subst n m} σ\n            -\u003e apply Φ (apply Ψ σ) ≡ apply (Φ ∘ˢ Ψ) σ\napply-apply (Var i) = refl\napply-apply (σ ⇒ τ) = cong₂ _⇒_ (apply-apply σ) (apply-apply τ)\n\nmapᶜ-mapᶜ : ∀ {n m p l} {g : Type m -\u003e Type p} {f : Type n -\u003e Type m} (Γ : Con n l)\n          -\u003e mapᶜ g (mapᶜ f Γ) ≡ mapᶜ (g ∘ f) Γ\nmapᶜ-mapᶜ  ε      = refl\nmapᶜ-mapᶜ (Γ ▻ σ) = cong (_▻ _) (mapᶜ-mapᶜ Γ)\n```\n\nIn one of the previous version I avoided rewriting, but the code was unreadable.\n\nUnification is postulated to be `TERMINATING`:\n\n```agda\n{-# TERMINATING #-}\nunify : ∀ {n} -\u003e (σ τ : Type n) -\u003e Maybe (∃ λ (Ψ : Subst n n) -\u003e apply Ψ σ ≡ apply Ψ τ)\n```\n\nIt can be proved so using the techniques from [6].\n\nThere are highly unsafe things in the [STLC.Experimental.Unsafe](src/STLC/Experimental/Unsafe.agda) module, but they are not used in the Algotihm M itself, only to define `on-typed` and its derivatives presented above.\n\n## References\n\n1. Martin Grabmüller. [Algorithm W Step by Step](https://github.com/wh5a/Algorithm-W-Step-By-Step)\n2. Oukseh Lee, Kwangkeun Yi. [A Generalized Let-Polymorphic Type Inference Algorithm](http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.41.6832)\n3. Dr. Gergő Érdi. [Compositional Type Checking](http://gergo.erdi.hu/projects/tandoori/Tandoori-Compositional-Typeclass.pdf)\n4. Andreas Abel. [Normalization by Evaluation: Dependent Types and Impredicativity](http://www2.tcs.ifi.lmu.de/~abel/habil.pdf)\n5. Guillaume Allais. [Type and Scope Preserving Semantics](https://github.com/gallais/type-scope-semantics)\n6. Conor McBride. [First-Order Unification by Structural Recursion](http://citeseerx.ist.psu.edu/viewdoc/download;jsessionid=3E26A845A6124F33E00E24E3D1C6036C?doi=10.1.1.25.1516\u0026rep=rep1\u0026type=pdf)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feffectfully%2Fstlc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Feffectfully%2Fstlc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feffectfully%2Fstlc/lists"}