{"id":23694282,"url":"https://github.com/nooga/let-go","last_synced_at":"2026-05-22T22:07:35.194Z","repository":{"id":53014119,"uuid":"340518175","full_name":"nooga/let-go","owner":"nooga","description":"Almost Clojure written in Go.","archived":false,"fork":false,"pushed_at":"2026-03-28T02:40:51.000Z","size":7071,"stargazers_count":81,"open_issues_count":2,"forks_count":9,"subscribers_count":3,"default_branch":"main","last_synced_at":"2026-03-28T08:39:08.203Z","etag":null,"topics":["clojure","compiler","go","interpreter","scripting-language","vm"],"latest_commit_sha":null,"homepage":"https://nooga.github.io/let-go","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nooga.png","metadata":{"files":{"readme":"README.md","changelog":null,"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":"2021-02-19T23:55:02.000Z","updated_at":"2026-03-26T09:32:30.000Z","dependencies_parsed_at":"2024-06-19T00:22:12.817Z","dependency_job_id":"c09785aa-74a5-4b4a-9b18-83f013f426a3","html_url":"https://github.com/nooga/let-go","commit_stats":{"total_commits":230,"total_committers":3,"mean_commits":76.66666666666667,"dds":0.008695652173912993,"last_synced_commit":"7d846abf6223c1c95bbc8341f38e36f200ddc5fe"},"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/nooga/let-go","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nooga%2Flet-go","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nooga%2Flet-go/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nooga%2Flet-go/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nooga%2Flet-go/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nooga","download_url":"https://codeload.github.com/nooga/let-go/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nooga%2Flet-go/sbom","scorecard":{"id":693959,"data":{"date":"2025-08-11","repo":{"name":"github.com/nooga/let-go","commit":"73db05b2c31dd57c8b61c4d86c4f78e77a190acc"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":4,"checks":[{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"SAST","score":10,"reason":"SAST tool detected: CodeQL","details":["Info: SAST configuration detected: CodeQL","Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/codeql-analysis.yml:1","Warn: no topLevel permission defined: .github/workflows/go.yml:1","Info: topLevel 'contents' permission set to 'read': .github/workflows/pages.yml:11","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Pinned-Dependencies","score":1,"reason":"dependency not pinned by hash detected -- score normalized to 1","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql-analysis.yml:38: update your workflow using https://app.stepsecurity.io/secureworkflow/nooga/let-go/codeql-analysis.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql-analysis.yml:42: update your workflow using https://app.stepsecurity.io/secureworkflow/nooga/let-go/codeql-analysis.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql-analysis.yml:53: update your workflow using https://app.stepsecurity.io/secureworkflow/nooga/let-go/codeql-analysis.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql-analysis.yml:67: update your workflow using https://app.stepsecurity.io/secureworkflow/nooga/let-go/codeql-analysis.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/go.yml:15: update your workflow using https://app.stepsecurity.io/secureworkflow/nooga/let-go/go.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/go.yml:17: update your workflow using https://app.stepsecurity.io/secureworkflow/nooga/let-go/go.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/go.yml:22: update your workflow using https://app.stepsecurity.io/secureworkflow/nooga/let-go/go.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/go.yml:25: update your workflow using https://app.stepsecurity.io/secureworkflow/nooga/let-go/go.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/pages.yml:24: update your workflow using https://app.stepsecurity.io/secureworkflow/nooga/let-go/pages.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/pages.yml:26: update your workflow using https://app.stepsecurity.io/secureworkflow/nooga/let-go/pages.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/pages.yml:32: update your workflow using https://app.stepsecurity.io/secureworkflow/nooga/let-go/pages.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/pages.yml:44: update your workflow using https://app.stepsecurity.io/secureworkflow/nooga/let-go/pages.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/pages.yml:46: update your workflow using https://app.stepsecurity.io/secureworkflow/nooga/let-go/pages.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/pages.yml:49: update your workflow using https://app.stepsecurity.io/secureworkflow/nooga/let-go/pages.yml/main?enable=pin","Warn: containerImage not pinned by hash: Dockerfile:1","Warn: containerImage not pinned by hash: Dockerfile:9: pin your Docker image by updating alpine:latest to alpine:latest@sha256:4bcff63911fcb4448bd4fdacec207030997caf25e9bea4045fa6c8c44de311d1","Info:   0 out of  13 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   1 third-party GitHubAction dependencies pinned","Info:   0 out of   2 containerImage dependencies pinned","Info:   1 out of   1 goCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Vulnerabilities","score":5,"reason":"5 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-pxg6-pf52-xh8x","Warn: Project is vulnerable to: GHSA-rrr8-f88r-h8q6","Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw","Warn: Project is vulnerable to: GHSA-m6fv-jmcg-4jfg"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-22T03:05:10.448Z","repository_id":53014119,"created_at":"2025-08-22T03:05:10.448Z","updated_at":"2025-08-22T03:05:10.448Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31311011,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-02T12:59:32.332Z","status":"ssl_error","status_checked_at":"2026-04-02T12:54:48.875Z","response_time":89,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5: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":["clojure","compiler","go","interpreter","scripting-language","vm"],"created_at":"2024-12-30T04:20:59.133Z","updated_at":"2026-05-22T22:07:35.166Z","avatar_url":"https://github.com/nooga.png","language":"Go","funding_links":[],"categories":["Clojure-likes"],"sub_categories":["[let-go](https://github.com/nooga/let-go)"],"readme":"\u003c!--suppress ALL --\u003e\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"meta/logo.png\" alt=\"Squishy loafer\" title=\"Squishy loafer of let-go\" /\u003e\n\u003c/p\u003e\n\n![Tests](https://github.com/nooga/let-go/actions/workflows/go.yml/badge.svg)\n\n# let-go\n\nGreetings loafers! _(λ-gophers haha, get it?)_\n\nlet-go is a Clojure dialect with a bytecode compiler and stack VM, written in Go.\nA single ~10.7MB binary, ~7ms cold start, no JVM. It passes the\n[jank-lang test suite](https://github.com/jank-lang/clojure-test-suite).\n\nI started this in 2021 as an elaborate joke: an excuse to write Clojure while\npretending to write Go. It turned out useful. I use it for CLIs, scripts, and\nweb servers, and I built [a daemonless container runtime](https://github.com/nooga/lgcr)\non top of it. You can compile let-go programs to standalone binaries or\nself-contained WASM web pages. [It even runs on Plan 9](https://x.com/MGasperowicz/status/2052428420592599507?s=20),\nand ReMarkable 2.\n\nIt is not a drop-in replacement for Clojure JVM. It does not load JARs and\ndoes not aim to. Most idiomatic Clojure code runs unmodified, but a real\nproject with library dependencies will need adjustments. See\n[Known limitations](#known-limitations) below.\n\n## Goals (in no particular order)\n\n- [x] Quality entertainment\n- [x] Implement most of Clojure: persistent data structures, lazy seqs,\n      transducers, protocols, records, multimethods, core.async, BigInts\n- [x] Comfy two-way Go interop (functions, structs, channels)\n- [x] AOT compilation to bytecode and standalone binaries\n- [x] Boot the runtime inside a single `requestAnimationFrame` (10ms left over at 60fps)\n- [x] Compile programs to self-contained WASM web pages with terminal emulation\n- [ ] Make it legal to write Clojure at your Go dayjob\n- [ ] nREPL in the browser (let-go VM in WASM, editor over WebSocket)\n- [ ] Stretch: let-go bytecode → Go translation\n\nNon-goals: drop-in JVM Clojure replacement; linter/formatter for Clojure-at-large.\n\n## Benchmarks\n\nlet-go vs Babashka, Joker, [go-joker](https://github.com/rcarmo/go-joker),\n[gloat](https://github.com/gloathub/gloat), and Clojure JVM. All benchmark\nfiles are valid Clojure that runs unmodified. Apple M1 Pro.\n\n|                 | let-go     | babashka | joker | go-joker | gloat | clojure JVM |\n| --------------- | ---------- | -------- | ----- | -------- | ----- | ----------- |\n| **Binary size** | **10.7MB** | 68MB     | 26MB  | 32MB     | 26MB  | 304MB (JDK) |\n| **Startup**     | **6.7ms**  | 18ms     | 12ms  | 13ms     | 16ms  | 363ms       |\n| **Idle memory** | **13.5MB** | 27MB     | 22MB  | 23MB     | 23MB  | 92MB        |\n\nlet-go wins decisively on the small things: smallest binary, fastest startup\n(~50× under JVM, ~3× under Babashka), lowest memory. It also wins on\nshort-lived data work like map/filter (7.9ms vs Babashka's 21.5ms) and\npersistent maps (20.8ms vs 23.7ms).\n\nOn bigger numerical workloads other implementations pull ahead. go-joker's\nWASM JIT compiles inner numeric loops and beats us on fib (1.47s vs 2.08s),\ntak, reduce, and transducers. The JVM dominates on long compute runs once\nHotSpot warms up. We're about even with Babashka on most algorithmic\nbenchmarks and 10×+ faster than upstream Joker (bytecode VM vs tree-walk).\n\nFull per-benchmark numbers and methodology:\n[benchmark/results.md](benchmark/results.md).\n\n## Compatibility\n\nTested against [jank-lang/clojure-test-suite](https://github.com/jank-lang/clojure-test-suite):\n**5621 / 5621 assertions pass** across 232 files through the `:clj` reader\nlens, with no known failures, compile skips, panic skips, or runtime skips.\n\n### Standard namespaces\n\n| Namespace            | Status                                                                                                                                                                                        |\n| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `clojure.core`       | macros, destructuring, lazy seqs, transducers, protocols, records, `deftype`, `reify`, multimethods, hierarchies, atoms, regex, metadata, BigInt, BigDecimal                                   |\n| `clojure.string`     | full                                                                                                                                                                                          |\n| `clojure.set`        | full                                                                                                                                                                                          |\n| `clojure.walk`       | `prewalk`, `postwalk`, `keywordize-keys`, `stringify-keys`, `walk`                                                                                                                            |\n| `clojure.edn`        | `read`, `read-string`                                                                                                                                                                         |\n| `clojure.pprint`     | `pprint`, `cl-format`                                                                                                                                                                         |\n| `clojure.test`       | `deftest`, `is`, `testing`, `are`, fixtures                                                                                                                                                   |\n| `clojure.core.async` | channels, `go`/`go-loop`, `alts!`, `mult`/`pub`, `pipe`/`merge`/`split` (real goroutines, not IOC)                                                                                            |\n| `io`                 | polymorphic readers/writers, `slurp`/`spit`, lazy line-seq, encoding, URLs, `with-open`                                                                                                       |\n| `http`               | Ring-style server + client, streaming responses                                                                                                                                               |\n| `json`               | `read-json`, `write-json` (float-preserving, record-aware)                                                                                                                                    |\n| `transit`            | transit+json codec with rolling cache                                                                                                                                                         |\n| `os`                 | `sh`, `stat`, `ls`, `cwd`, `getenv`/`setenv`, `exit`, `os-name`, `arch`, `user-name`, `hostname`, separators                                                                                  |\n| `System`             | JVM-shaped: `getProperty`, `getProperties`, `getenv`, `exit`, `currentTimeMillis`, `nanoTime`. Exposes `let-go.version`, `let-go.commit`, `user.home`, `user.dir`, `os.name`, `os.arch`, etc. |\n| `syscall`            | direct Linux syscalls (mount, unshare, mknod, prctl, capset, seccomp, AppArmor)                                                                                                               |\n| `pods`               | Babashka pods over JSON / EDN / transit                                                                                                                                                       |\n\n### Babashka pods\n\nlet-go can load [Babashka pods](https://github.com/babashka/pods), which\nopens up the whole pod ecosystem: SQLite, AWS, Docker, file watching, etc.\n\n```clojure\n(pods/load-pod 'org.babashka/go-sqlite3 \"0.3.13\")\n\n(pod.babashka.go-sqlite3/execute! \"app.db\"\n  [\"create table users (id integer primary key, name text)\"])\n(pod.babashka.go-sqlite3/query \"app.db\"\n  [\"select * from users\"])\n;; =\u003e [{:id 1 :name \"Alice\"}]\n```\n\nIt shares `~/.babashka/pods/` with `bb`, so install pods with babashka and use\nthem from `lg`. See the [pod registry](https://github.com/babashka/pod-registry)\nfor what's available.\n\n## Known limitations\n\n### Not implemented\n\n- **STM coordination**: `ref`/`dosync`/`alter`/`commute` are atom-backed compatibility aliases, not coordinated STM\n- **Asynchronous agents**: `agent`/`send`/`send-off` are synchronous atom-backed compatibility aliases\n- **Chunked sequences**: lazy seqs are unchunked\n- **Custom tagged literal readers**: built-in `#uuid` and `#inst` work; unknown tags read as their payload, and `*data-readers*` / `*default-data-reader-fn*` are not implemented\n- **Java-style `deftype` / `reify` method bodies and host interfaces**: protocol implementations work; JVM host methods do not\n- **Spec** (no `clojure.spec`)\n- **`subseq` / `rsubseq`**: sorted collections work (`sorted-map`, `sorted-set`, `rseq`); range queries don't\n\n### Behavioral differences\n\n- `concat*` (used internally by quasiquote) is eager; user-facing `concat` is lazy\n- `\u003c!` / `\u003c!!` are identical, same for `\u003e!` / `\u003e!!` (Go channels always block)\n- `go` blocks are real goroutines, not IOC state machines (cheaper, and they can call blocking ops directly)\n- Numeric tower is pragmatic: `int64`, `float64`, `BigInt`, ratios, and `BigDecimal`, without the JVM's full primitive/class model\n- Base integer `+`/`-`/`*`/`inc`/`dec` throw on overflow; use `+'`/`-'`/`*'`/`inc'`/`dec'` for BigInt-promoting exact math\n- Regex is Go flavor (`re2`), not Java regex\n- `letfn` uses atoms internally for forward references\n\n## Examples\n\nThings written in let-go:\n\n- [**xsofy**](https://github.com/nooga/xsofy): a roguelike that runs in the browser and the terminal from the same source\n- [**lgcr**](https://github.com/nooga/lgcr): a daemonless container runtime, built on the `syscall` namespace\n\nIn this repo:\n\n- [examples/](https://github.com/nooga/let-go/tree/main/examples): small programs\n- [test/](https://github.com/nooga/let-go/tree/main/test): `.lg` test files\n\n## Try it online\n\n[Bare-bones browser REPL](https://nooga.github.io/let-go/), running a WASM\nbuild of let-go.\n\n## Install\n\n### Homebrew (macOS / Linux)\n\n```bash\nbrew install nooga/let-go/let-go\n```\n\n### Download\n\nPrebuilt binaries for Linux, macOS, and Plan 9 in\n[Releases](https://github.com/nooga/let-go/releases).\n\n### From source (Go 1.22+)\n\n```bash\ngo install github.com/nooga/let-go@latest\n```\n\nThis installs the command as `let-go`. Homebrew and release archives install\nthe same CLI as `lg`.\n\n### Usage\n\n```bash\nlg                                # REPL\nlg -e '(+ 1 1)'                   # eval expression\nlg myfile.lg                      # run file\nlg -r myfile.lg                   # run file, then REPL\n```\n\n## Compile and distribute\n\nlet-go can compile programs to bytecode (`.lgb` files) and bundle them as\nstandalone executables.\n\n```bash\nlg -c app.lgb app.lg              # compile to bytecode\nlg app.lgb                        # run bytecode\n\nlg -b myapp app.lg                # bundle into a self-contained binary\n./myapp                           # runs anywhere, no lg needed\n```\n\nThe standalone binary is a copy of `lg` with your bytecode appended. Copy it\nto another machine and it runs.\n\n```bash\nlg -w site app.lg                 # compile to a WASM web app\nopen site/index.html\n```\n\nThe output is a self-contained `index.html` (~6MB, inlined WASM, gzipped)\nplus a service worker that supplies the COOP/COEP headers GitHub Pages needs\nfor SharedArrayBuffer. Programs that use the `term` namespace get full\nterminal emulation via xterm.js: ANSI colors, cursor positioning, raw\nkeyboard input.\n\nThe `*compiling-aot*` var is `true` during `-c`/`-b`/`-w` compilation and\n`false` at runtime, useful for keeping side effects out of compile time:\n\n```clojure\n(defn -main []\n  (start-server))\n\n(when-not *compiling-aot*\n  (-main))\n```\n\n`*in-wasm*` is `true` when running inside a WASM build.\n\n## nREPL\n\nlet-go ships an nREPL server that works with CIDER (Emacs), Calva (VS Code),\nand Conjure (Neovim).\n\n```bash\nlg -n                             # default port 2137\nlg -n -p 7888\n```\n\nIt writes `.nrepl-port` to the working directory so editors auto-discover it.\n\nSupported ops: `clone`, `close`, `eval`, `load-file`, `describe`,\n`completions`, `complete`, `info`, `lookup`, `ls-sessions`, `interrupt`.\n\n- **Emacs (CIDER)**: `M-x cider-connect-clj`, `localhost`, port from `.nrepl-port`\n- **VS Code (Calva)**: open a let-go project (the bundled `.vscode/settings.json` registers a connect sequence). Use \"Calva: Start a Project REPL and Connect (Jack-In)\" → \"let-go\", or \"Calva: Connect to a Running REPL Server\" if nREPL is already up.\n- **Neovim (Conjure)**: auto-connects when `.nrepl-port` exists.\n\n## Embedding in Go\n\nlet-go embeds cleanly as a scripting layer for Go programs. Define Go values\nand functions, hand them to the VM, run user-supplied Clojure against your\ndata. Go structs roundtrip as records, Go channels are first-class let-go\nchannels, and Go functions are callable from let-go.\n\n```go\nimport (\n    \"github.com/nooga/let-go/pkg/api\"\n    \"github.com/nooga/let-go/pkg/vm\"\n)\n\nc, _ := api.NewLetGo(\"myapp\")\n\nc.Def(\"x\", 42)\nc.Def(\"greet\", func(name string) string {\n    return \"Hello, \" + name\n})\n\nv, _ := c.Run(`(greet \"world\")`)\nfmt.Println(v) // \"Hello, world\"\n```\n\nRegistered structs become records on the let-go side. Unmutated values unbox\nback to the original Go type for free; mutated ones go through `vm.ToStruct[T]`.\n\n```go\ntype Item struct{ Name string; Price float64; Qty int }\nvm.RegisterStruct[Item](\"myapp/Item\")\n\nc.Def(\"item\", Item{Name: \"Widget\", Price: 9.99, Qty: 5})\nc.Run(`(:name item)`)                  // \"Widget\"\nc.Run(`(* (:price item) (:qty item))`) // 49.95\n```\n\nGo channels and `vm.Chan` plug into `go` / `\u003c!` / `\u003e!` directly:\n\n```go\ninch := make(chan int)\noutch := make(vm.Chan)\nc.Def(\"in\", inch)\nc.Def(\"out\", outch)\n\nc.Run(`(go (loop [i (\u003c! in)]\n             (when i\n               (\u003e! out (inc i))\n               (recur (\u003c! in)))))`)\n```\n\n[`pkg/api/interop_test.go`](pkg/api/interop_test.go) has the full set of\nembedding examples (defs, structs, channels, function calls).\n\n## Testing\n\n```bash\ngo test ./... -count=1 -timeout 30s\n```\n\n## Contributing\n\n### Git merge driver for `core_compiled.lgb` (one-time setup)\n\n`pkg/rt/core_compiled.lgb` is a binary bundle regenerated from the embedded\n`.lg` sources. Git cannot meaningfully merge this binary on rebase, so we ship\na custom merge driver that regenerates from sources after the `.lg` files have\nbeen merged as text.\n\nAfter cloning the repo (or pulling for the first time after this driver was\nadded), register it locally:\n\n```bash\ngit config merge.lgb.name \"Regenerate core_compiled.lgb from sources\"\ngit config merge.lgb.driver \"scripts/git-merge-lgb.sh %O %A %B %L %P\"\n```\n\nThis is a one-time per-clone step. After registration, rebases that touch any\nembedded `.lg` source will regenerate the `.lgb` automatically — no more\nbinary merge conflicts when stacking PRs that edit `core.lg` and friends.\n\n---\n\nEver wanted a 20MB pure-Go JS runtime that typechecks and runs TypeScript?\nCheck my other project: https://github.com/nooga/paserati\n\n[🤓 Follow me on X](https://x.com/MGasperowicz)\n[🐬 Check out monk.io](https://monk.io)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnooga%2Flet-go","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnooga%2Flet-go","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnooga%2Flet-go/lists"}