{"id":50836161,"url":"https://github.com/wingleeio/inga","last_synced_at":"2026-06-14T03:07:44.155Z","repository":{"id":363915553,"uuid":"1265514173","full_name":"wingleeio/inga","owner":"wingleeio","description":"The Inga language: typed errors and inferred dependencies as effect rows, Effect.ts semantics in Koka-style direct syntax. Interpreter + LLVM native backend, formatter, and LSP — in Rust.","archived":false,"fork":false,"pushed_at":"2026-06-10T22:41:11.000Z","size":592,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-10T23:21:17.539Z","etag":null,"topics":["compiler","effect-system","language","llvm","lsp","rust"],"latest_commit_sha":null,"homepage":null,"language":"Rust","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/wingleeio.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-10T20:59:52.000Z","updated_at":"2026-06-10T22:41:15.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/wingleeio/inga","commit_stats":null,"previous_names":["wingleeio/inga"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/wingleeio/inga","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wingleeio%2Finga","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wingleeio%2Finga/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wingleeio%2Finga/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wingleeio%2Finga/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wingleeio","download_url":"https://codeload.github.com/wingleeio/inga/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wingleeio%2Finga/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34307724,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-14T02:00:07.365Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["compiler","effect-system","language","llvm","lsp","rust"],"created_at":"2026-06-14T03:07:44.059Z","updated_at":"2026-06-14T03:07:44.149Z","avatar_url":"https://github.com/wingleeio.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Inga\n\n**Typed errors. Inferred dependencies. Direct style.**\nInga is what Effect.ts would look like as its own language, with Koka's\ndirect style: you write ordinary code, and the compiler infers — and\nenforces — what can fail (`!` row) and what it needs (`uses` row). Data is\nstructs and enums; `fail` raises *any* value, and the `!` row names the\ntypes of the values a function can fail with.\n\n```inga\nuse std/json\nuse std/schedule\n\nstruct UserNotFound = { Int id }\nstruct DbError      = { String cause }\nstruct CacheMiss    = { String key }\n\n// Fully annotated — but every annotation here is optional and inferred:\ngetUserById :: (Int id) -\u003e User ! UserNotFound uses Database, Cache, Logger {\n    match cached(id) {\n        Some(user) -\u003e user\n        None       -\u003e fetchAndCache(id)\n    }\n}\n\nfetchAndCache :: (id) {\n    Database db          // ← binds the capability, infers `uses Database`\n    Cache cache\n    Logger logger\n\n    user = db.findUser(id)\n        |\u003e retry(schedule.exponential(100.millis) |\u003e schedule.upTo(3))\n        |\u003e orFail(UserNotFound(id))\n        |\u003e catch {\n            DbError(cause) -\u003e {\n                logger.warn(\"db down after retries: ${cause}\")\n                fail UserNotFound(id)\n            }\n        }\n\n    cache.set(\"user:${id}\", json.encode(user), 5.minutes) |\u003e ignoreFailure\n    logger.info(\"cache refreshed for ${id}\")\n    user\n}\n```\n\nHover `fetchAndCache` in an editor and the language server shows what the\ncompiler inferred:\n\n```\nfetchAndCache :: (Int id) -\u003e User ! UserNotFound uses Cache, Database, Logger\n```\n\n`main` must have empty rows — every error caught, every service provided —\nso a program that compiles cannot hit an unhandled typed error or a missing\ndependency:\n\n```inga\nmain :: () {\n    provide consoleLogger, memoryCache, fakeDb {\n        user = getUserById(42) |\u003e catch { UserNotFound -\u003e User(0, \"anonymous\", \"n/a\") }\n        println(\"fetched: ${user.name}\")\n    }\n}\n```\n\n## Try it\n\n```sh\ncargo run -p inga-cli -- run examples/user_service.inga\n```\n\n```\n[info] cache refreshed for 42\nfetched: Wing \u003cwing@anara.com\u003e\ncached:  Wing\nfallback for user 7: anonymous\n```\n\nThe flaky fake database refuses the first two connections — `retry` recovers\n— and the second lookup hits the cache. Delete the `catch` in `main` and the\ncompiler answers:\n\n```\nerror: `main` does not handle the error `UserNotFound`; add a `catch` for it\n```\n\n## The toolchain\n\nOne binary, everything included:\n\n| Command | What it does |\n|---|---|\n| `inga run file.inga` | type-check, compile, and run (a temp native binary) |\n| `inga build file.inga [-o out]` | **compile to a native binary** (LLVM IR → clang -O2) |\n| `inga check files...` | diagnostics with source carets |\n| `inga test [files...]` | run `test*` functions; `assert`/`assertEq` failures point at the line |\n| `inga fmt [--check] files...` | canonical formatter (idempotent, keeps comments) |\n| `inga highlight file.inga` | ANSI syntax highlighting in the terminal |\n| `inga lsp` | language server: hover on **everything a cursor lands on** (inferred `!`/`uses` rows, struct fields like `req.path`, record-literal keys, constructors, container methods, tuple slots, suffixes — binding types reflect the whole function's inference), diagnostics, go-to-definition, completion with **auto-import** (sibling modules + `std/*`), **`.`-member completion** (module members, struct fields, service methods, map/list ops, tuple slots, duration suffixes), **field-name completion inside `User { … }` literals**, **arm completion** in `catch`/`match` (the row's error types, the scrutinee's variants), quick fixes on unknown names (cmd+.), formatting, semantic tokens |\n\nEditor support lives in [`editors/vscode`](editors/vscode) (TextMate grammar\n+ LSP client).\n\n## Concurrency without a manual\n\nConcurrency is a standard module, `std/fiber`, and the effect system does\nthe bookkeeping. One `provide Runtime(4)` in `main`, then:\n\n```inga\nuse std/fiber\n\na = crunch(\"medium\", 100000) |\u003e fiber.fork    // crunch :: ... -\u003e Report ! TooBig\nb = crunch(\"large\", 10000000) |\u003e fiber.fork   // both in flight; zero locks\npair = fiber.join((a, b)) |\u003e catch { TooBig -\u003e (fallback, fallback) }\n\ntotals = fiber.parMap(urls, (u) -\u003e fetch(u) |\u003e fiber.settle)   // [Outcome\u003c...\u003e]\n```\n\nErrors travel in the fiber's type (`Fiber\u003cReport ! TooBig\u003e`, visible on\nhover) and **re-raise at the join** — catch them where the result lands, or\nthe compiler reminds you the same way it guards `main`. `join` is\nstructural (a fiber, a tuple of fibers, or a list), `settle` turns the\nerror channel into data (`Outcome` — match `Ok`/`Failed` per element), and\n`race`/`within` give deadlines. Services cross into fibers only when\ndeclared `shared service` (scalar-only state, checked at every impl);\ncaptured values are frozen so refcounts never race; an unjoined fiber is\ninterrupted when its handle drops — no leaks, no daemons. Parallelism shows\nup in the row (`uses Fibers`), so a library that forks says so in its\ntypes. Try it: `inga run examples/fibers.inga` — and\n[`examples/fiber_errors.inga`](examples/fiber_errors.inga) walks every\nerror placement, one section each.\n\n## Tests are built in\n\n```sh\ninga test games/logic_test.inga\n```\n\nEvery zero-parameter `test*` function is a test; `assert(cond)` and\n`assertEq(actual, expected)` are ordinary typed errors (`! AssertionError`),\nso a failing assertion prints with the usual caret pointing at the line.\nINGA-LATRO's poker evaluator is tested this way.\n\n## The network is in your types\n\n`std/http` is the HTTP client — and `uses Http` in a signature is how you\nknow a function touches the network, the same honesty as every other\ncapability. A non-2xx status is data (like fetch); only transport failures\nraise `! HttpError`; streaming is a pull loop; deadlines and backoff are\nthe combinators you already have:\n\n```inga\nuse std/http\n\nfetchPrice :: (String sym) -\u003e Float ! HttpError, TimeoutError uses Http, Fibers {\n    resp = http.get(\"https://api.example/price/${sym}\")\n        |\u003e fiber.within(2.seconds)\n        |\u003e retry(schedule.exponential(100.millis) |\u003e schedule.upTo(3))\n    resp.body |\u003e parseInt |\u003e getOrElse(0) |\u003e toFloat\n}\n```\n\nTry it: `inga run examples/http_client.inga` — or\n[`examples/pokedex.inga`](examples/pokedex.inga): a paginated pokédex of\nall 151 gen-1 pokémon with real sprites — pages fetch on background fibers\n(`fiber.poll` per frame, the render fiber never parks), sprite PNGs become\ntextures via `graphics.imageNew`, and prev/next page through the API.\n\nAnd `std/http` serves too — `http.serve(port, handler)` where the handler\nis an ordinary `(HttpRequest) -\u003e HttpResponse` function: it captures\nservices like any closure (`uses Counter` shows up in its row), and a\nhandler failure answers that client 500 *and re-raises at the serve\nsite* — so a silently crash-looping server is unrepresentable; catch in\nthe handler to keep serving. Routing is **string-template matching** —\nno router library, just patterns:\n\n```inga\nmatch req.path {\n    \"/visit\"           -\u003e visit()\n    \"/users/${Int id}\" -\u003e getUser(id)   // binds id when the segment parses\n    _                  -\u003e notFound()\n}\n```\n\nTry it, then curl localhost:8080/visit and localhost:8080/users/42:\n`inga run examples/server.inga`.\n\nAnd middleware is just functions — including middleware that **provides**.\nA callback type may carry a `uses` row; such callbacks get their\ncapabilities at each call, so a `provide` around the call site reaches\nthem:\n\n```inga\ntype Authed = (HttpRequest) -\u003e HttpResponse uses Session\n\nwithAuth :: (Authed inner) {\n    (req) -\u003e {\n        provide loggedIn(authenticate(req))   // a parameterized impl: per-request state\n        inner(req)                            // the handler's `uses Session`, satisfied here\n    }\n}\n\nmain :: () {\n    app = dashboard |\u003e withAuth |\u003e withLogging   // main never provides Session\n    http.serve(8080, app)\n}\n```\n\nSessions live exactly as long as one request, setup failures are typed\nerrors at the provide site, and `service Session { User user }` exposes\nthe value directly (`session.user`, no getter). Try it:\n`inga run examples/middleware.inga`, then curl /dash?token=42.\n\nThe disk works the same way: `std/fs` is the file system, `uses Fs` in a\nsignature is how you know a function touches it, and failures raise\n`IoError { path, message }` — the path rides in the error.\n`fs.read/write/append/exists/list/remove/createDir` for whole files, plus\n**file handles** (`fs.open/readAt/writeAt/size/sync/close`) for the\nrandom-access tier — positional I/O against `File { handle }`, `sync` for\nwrite-ahead discipline. All blocking on the calling fiber, all binary-safe\n(a file body and an http body are the same kind of string). Try it:\n`inga run examples/notes.inga`.\n\nBelow HTTP sits `std/net` — raw TCP (`net.connect/listen/accept/read/\nwrite/close/stop` behind `provide Net`, `! NetError`), and with the\nbits-and-bytes builtins (`bitAnd/shiftL/byteAt/intToBytes/bytesToInt/…`)\ncustom wire protocols and storage formats are ordinary code. `MutList`\njoins `MutMap` for in-place mutation, `std/time` is the wall clock\n(`time.now()`, `time.utc`, `time.iso`), and `readLine()` means stdin\nexists — pipes and prompts both work.\n\nAnd the terminal is a capability too: `std/term` gives raw mode, decoded\nkey events (`up`/`enter`/`ctrl+c`/…), and the window size, with the\nruntime restoring the terminal on every exit path. ANSI goes through\nplain `print` (`\\e` is ESC). A TUI is ~40 lines:\n`inga run examples/picker.inga`.\n\n## Graphics, and a game\n\nInga has GL-backed graphics bindings — the `std/graphics` module (window, rects,\ncircles, text, mouse, **GLSL fragment shaders**), implemented on OpenGL via\nminiquad/macroquad in the native runtime and available in both backends. The\nframe loop is owned by the runtime (`graphics.run(w, h, title, frame)` calls your\nclosure once per frame), so games don't need unbounded recursion — and the\nframe closure captures capability evidence like any other Inga closure.\n\nThe proof is [`games/balatro.inga`](games/balatro.inga): **INGA-LATRO**, a\nBalatro-style roguelike deckbuilder in ~1,000 lines of pure Inga, split\nacross six modules (`use game`, `util`, `cards`, `jokers`, `poker`, `state`) —\npoker-hand scoring, escalating blinds and antes, fifteen jokers and a\nrerollable shop, animated card deals/hovers/score popups, and the signature\nswirling paint background written as a GLSL shader *inside the Inga source*\nand compiled at runtime via `graphics.shaderNew` — plus blind-intro\nbanners, sellable jokers (hover one), win-screen confetti, and a freely\nresizable window (every Inga game letterboxes for free). All game state lives in\none `Game` service ([`games/game.inga`](games/game.inga)) provided once in\n`main` — every function just says `uses Game` (inferred), so nothing\nthreads state through arguments, and the\n[`inga test` tests](games/logic_test.inga) provide their own fresh instance\nper test. The frame closure opens with `provide Arena(256.kb)`, so\neverything built while drawing is freed wholesale at frame end — the render\npath does no refcount work:\n\n```sh\ninga build games/balatro.inga -o ingalatro \u0026\u0026 ./ingalatro\n```\n\n![INGA-LATRO](games/screenshot.png)\n\n## Repository layout\n\n```\ncrates/inga-core      lexer, parser, type \u0026 effect inference, formatter\ncrates/inga-codegen   LLVM backend (emits .ll; clang compiles and links)\ncrates/inga-rt        native runtime staticlib (allocator, strings, maps, clock)\ncrates/inga-cli       the `inga` binary\ncrates/inga-lsp       language server (lsp-server / lsp-types)\neditors/vscode        VS Code extension + TextMate grammar\neditors/zed           Zed extension (tree-sitter highlighting + LSP)\ntree-sitter-inga      tree-sitter grammar (used by the Zed extension)\nexamples/             hello.inga, retry.inga, shapes.inga, arena.inga, fibers.inga, fiber_errors.inga, http_client.inga, pokedex.inga, notes.inga, server.inga, middleware.inga, picker.inga, modules.inga (+ geometry.inga), user_service.inga\ngames/                balatro.inga (+ game, util, cards, jokers, poker, state, logic_test) — a Balatro-style deckbuilder\nbench/                the same workloads in Inga, JavaScript, and Rust (see bench/README.md)\ndocs/SPEC.md          language design: semantics, effect rows, execution strategy\n```\n\n`bench/run.sh` runs five identical workloads as Inga, node, and `rustc -O`\n— Inga wins every one against V8 ([results](bench/README.md)).\n\n## How it runs\n\nInga is compiled, always: `inga build` produces a binary through LLVM, and\n`inga run` is the same pipeline to a temp binary, executed immediately —\none backend, one semantics. Because Inga's effects are static, **the effect\nsystem compiles away**: error rows become Rust-style `{value, err}` two-register returns,\ncapability rows become Koka-style evidence parameters, and a capability\nmethod call is the same machine code as a Rust `dyn` call. Memory is\n**Perceus-style ARC** (non-atomic refcounts + compiler-emitted drop glue,\nsmall objects recycled through free lists, one heap per fiber so forking\nnever locks) with opt-in region arenas: `provide Arena(256.kb)` makes a\nscope allocate from a region freed wholesale at scope end, deep-copying the\nscope's result out as it dies. Details in\n[docs/SPEC.md §6](docs/SPEC.md#6-execution-how-inga-runs).\n\nThe result ([benchmarks](bench/README.md)): **compiled Inga beats Node/V8 on\nall five benchmark workloads** — 2–3× on raw calls, DI dispatch, and string\ninterpolation, ~290× on typed-error control flow — and beats idiomatic Rust\non two of them.\n\n## Status\n\nv0.4 — a complete, tested vertical slice: language (structs/enums/tuples,\nnamed-field construction and record update, generics, exhaustive `match`,\ntyped errors over any value),\ninference, a **native-only LLVM backend** (`show`/`==`/`json.encode`/`json.decode`/\nfunctions-as-values all compile; the reference interpreter served its\npurpose and was removed), Perceus-style ARC + arenas with copy-out,\n**`std/fiber` concurrency** (fork / structural join / settle / race /\nwithin, `shared` services, drop-is-supervision), a system-level std\n(`std/http` client **and server**, `std/fs` with random-access file\nhandles, `std/net` raw TCP, `std/json`, `std/process`, `std/time`,\n`std/term`, stdin, `MutList`, sort/string/number/bit/byte builtins), a\nbuilt-in test runner,\nformatter, LSP, editor tooling (`cargo test` covers all of it). Not yet: a\npackage manager, the M:N fiber scheduler (fibers run thread-per-fiber\ntoday — same semantics, that's the point of the `Runtime` promise),\nper-implementation capability precision, resumable handlers.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwingleeio%2Finga","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwingleeio%2Finga","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwingleeio%2Finga/lists"}