*let-go v1.7.0* - *λ-gophers, assemble* :hamster: Greetings friends! I've been crafting this on and off since 2021 out of pure spite and cope after I had to switch from Clojure to Go for my dayjob. The running joke was that this would make it legal to write Clojure while pretending to write Go. It's not perfect yet, but I can't hold it any longer - here it is. `let-go` is a bytecode compiler + VM for a Clojure-shaped language, shipped as a single *~10MB static binary* that cold-starts in *~7ms*. Currently the smallest and fastest-starting Clojure-family language in Go. 3x faster startup than Babashka, 2x faster than Joker, 48x faster than the JVM. The whole runtime boots inside one `requestAnimationFrame` and still has 10ms to spare at 60fps. Compute performance sometimes within 4% of Babashka, sometimes faster, sometimes slower. *Clojure compatibility:* *4696 / 4921 assertions pass (95.4%)* on the `jank-lang/clojure-test-suite` across 217 files. It won't run your existing Clojure project unmodified yet, but day-to-day idiomatic code reads, writes, and behaves the same - macros, destructuring, lazy seqs, transducers, protocols/records/multimethods, persistent maps & vectors, atoms, regex, BigInts, you name it. *The good stuff:* • `core.async` running on actual goroutines (no IOC state machine) • standard namespaces: `clojure.string`, `set`, `walk`, `edn`, `pprint`, `test`, `core.async`, plus `io`, `http`, `json`, `transit`, `os` • *Babashka pods* support - shares the `~/.babashka/pods` cache, `bb` installs, `lg` runs • `syscall` namespace - yes, you can write a container runtime in Clojure now • `lg` → REPL with syntax highlight and completions, `lg -n -p 2137`→ nREPL server for your favorite Emacs • `lg -c myapp.lgb main.lg` → portable bytecode blob • `lg -b myapp main.lg` → standalone binary • `lg -w outdir main.lg` → self-contained WASM web app with xterm.js terminal • Embeddable in Go: Go structs roundtrip as records, Go channels are first-class let-go channels, Go funcs are callable from let-go and vice versa *Real fun projects written in let-go:* • *Xs of Y -* a roguelike that runs in the browser *and* the terminal from the same lispy source: <https://github.com/nooga/xsofy> • *lgcr* - a decent daemonless container runtime for MacOS and Linux: <https://github.com/nooga/lgcr> *Try it online (WASM REPL):* <https://nooga.github.io/let-go/> *Install:* see GH README *Repo*: <https://github.com/nooga/let-go> Caveats, for now: no STM/refs/agents, sorted collections incomplete, no BigDecimal rounding, no `reify`/`deftype`, regex is Go flavor (re2), numeric overflow wraps silently. Full list in the README. Feedback, :star: s, issues, and PRs warmly welcomed. Join me at <#C04DCFZKPJN|let-go> :bread:
Holy moly that's nuts. Congrats on the release.
Cool, seems like go is quite the popular target ^^ How does this compare to #gloat?
Firing up a let-go REPL in Calva.
Major congrats on the release, @xnooga! 🎉 Totally mind-blowing work.
For that demo clip I wanted to do something the equivalent of (System/getProperty "babashka.version"). Is there?
Congrats writing Clojure at work again!
It seems we have a revival of Clojure on Go going on this year https://rcarmo.github.io/projects/go-joker/
I was totally unaware of this one!
need to benchmark against it - I might not be the fastest go impl 🤔
the loop example I tend to use for bb:
bb -e "(time (let [x 0 j 10000000] (loop [i 0 j 10000000] (if (zero? j) i (recur (inc i) (dec j))))))"
runs almost as fast on go-joker than JVM Clojure now (around 5-9ms, it's impressive)it remains to be seen how compatible go-clojure is when tested with the jank suite though
Updated Calva jack-in demo. Feel free to use in docs or wherever, @xnooga. I know I will 😃
https://www.linkedin.com/feed/update/urn:li:ugcPost:7458072712469479424/
I'll reshare this on LI, thank you!!
I remember when LI was a very boring thing where people just sent me messages to connect, usually recruiters. Somehow a lot of people fled from Twitter etc to there which now makes it the places where all my relatives, professional colleagues from the past and other people hang out.
@borkdude re perf - spoils of a mature WASM JIT target 😄
I ran your one liner on lg and it clocks 3x slower than bb- spoils of GraalVM 😉
go-joker seems to rip through loop-recur but folds under deep recursion and reductions. It crashes on my fib, tak and transduce benches.
I think my main goal was never some stellar compute perf - there is only so much that can be done without an optimizing JIT, which would be veeeery impractical in Go. I think tiny footprint and fastest possible boot are still pretty cool though 😁
> 3x slower than bb- spoils of GraalVM
it's faster on a normal JVM ;)
oh yes, I didn't mean to take away any of the joy of your announcement, I was just triggered by Snuffle's remark that go is quite a popular target for Clojure now it seems. performance isn't always the most important bit. it's cool that you support babashka pods. my mind immediately went: what if we can use let-go to build a standalone pod-as-binary instead of go itself, that would be cool too, then we can still use clojure
definitely something I'd like to try out!
it's quite easy to build standalone executables using the binary-blob+zip-file approach. bb uses this too.
@borkdude is LinkedIn now the new (old) Facebook for you?
I guess so, except that people talk more about their work there than their vacations
at least I can send people a DM when I need to, that's how I usually use it
I ditched FB, twitter and every other social network a long time ago. I still have LinkedIn, but zero notifications. It is such a spam machine, but for work it is sometimes difficult to get around it. Right now I am considering picking up my old Nokia 3310 again as I'm also getting flooded with whatsapp messages.
I still tend to not post much on LI compared to mastodon/bsky/twitter
I know someone who only has a dumb phone and not even a computer at home. He's ahead of the times!
the only reason I haven't done it yet is because my kids hate it if they cannot chat with me like that. But in honesty, I'm perfectly happy having a dumb phone
that's a luxury :d
wow, nice work! there is also https://github.com/glojurelang/glojure but not sure how it compares?
I think we now have joker + the above mentioned fork glojure + gloat(?) as a fork let-go
@knotschi it seems that let-go is the smallest and fastest booting, it's also much faster than joker and glojure while using less memory. It's not as complete as the gloat fork of glojure though.
Are #joker and go-joker different things?
go-joker is a fork with performance enhancements
it seems that @rcarmo forked joker and used its frontend to make a compiler targetting a WASM VM
ah, it landed just yesterday? 😄
it's in the air
it's funny how everyone is making their own clojure in Go and nobody seems to know about the others 😄
the lisp curse v2
I guess LLM plays a role in this too, lisp curse on steroids
I beginning to think that Clojure will win the mainstream by totally obliterating in the reach category.
I was aware of joker and later became aware of glojure, but the goal was to improve on Joker's performance and design while having some fun
Just saturate every language with a Clojure implementation until that becomes the obvious interface for LLMs regardless of runtime. The vibe coders won't even know it happened.
I think it's cool though! more people should write lisps and Clojure is the nicest (IMO) 😄
People working on other languages don't know why their LLM wants to build nREPL integration and an embedded clojure runtime, but it really wants to
"ok, let me build clojure + nrepl layer now for your language". <enter>
> $20k bill from Anthropic
worth it
My Rust-y Agical colleague is building a static site builder (a super interesting one, but that’s for another story). It’s now Hiccup all over and a growing Clojure implementation for config. There’s a REPL that Calva is happy to talk to. OK, part is that we’re colleagues and I go on and on about data and Hiccup. But I actually think that Claude has something to do with the going-all-in of it.
After all, Clojure is just a library you or your LLM can add to the dependencies of your project. ;)
So we have now evolved from writing every algorithm naively by ourselves into writing our own implementation of our favourite language? 😅
I only picked Clojure because I thought long and hard and could not come up with a better language, existing or imaginary
but langs are my thing kind of, I've been writing small interpreters and compilers for 20 years, now I can make them useful for others instead of being just toys
In the optimal language, you just require the LLM lib and run it, and it’ll be whatever app you need at that moment.
The good old "Make Everything Perfect" button. :)
LLM: you get only three wishes.
you know what, I've made this cool macro once:
(defyolo fetch-tweets [n & tags]
"fetch n latest tweets from twitter API tagged with tags")
it prompts the LLM to write the function body and caches it, you can then call (fetch-tweets 10 "#foo") and see if it workedworks nice in a REPL 😂
“For my first wish, I want infinite wishes” “No, that’s against the genie rules” “OK, then I want infinite genies. Also takes care of my second wish: more friends”
what if we took only clojure's stdlib with docstrings and then let an LLM one shot JVM Clojure
I don't think that would work really well based on how much gymnastics it took to get let-go stdlib going 😄
the problem, it probably already knows clojure
by now it probably does, although I've noticed it has huge problems with idiomatic code, both claude and gpt
but it would maybe a good test to see how good the docstrings are
they don't like destructuring, avoid threading macros, overly defensive agains nil, tend to generate long ass let-s and defns mixed with ifs etc.
and (boolean something) while it's completely unnecessary
https://scicloj.github.io/zulipdata/ is a new small library for pulling and analyzing data from the https://clojurians.zulipchat.com/. Alpha stage, current version 0.1.1.
*rechentafel 0.1.x — pure-Clojure spreadsheet interpreter*
Hi all — first public cut of org.replikativ/rechentafel is on Clojars.
A pure-Clojure formula engine: parser, per-cell dependency graph, incremental recalc, ~270 functions, Excel 365 dynamic arrays + LET / LAMBDA, structured table refs, 3D refs. No POI, no LibreOffice, no native deps at runtime.
- *Cross-platform*: same .cljc source on JVM and Node/browser cljs
- *Persistent*: workbook is an immutable value — set-cell shares structure with the parent, so forking is cheap
- *Seedable PRNG*: (assoc wb :rng-seed N) makes RAND/RANDARRAY deterministic
- *Optional .xlsx I/O* under the :poi alias (load + dynamic-array round-trip)
Verified against POI and LibreOffice on a benchmark suite. ~70× faster than headless LibreOffice on the 10k-cell shapes.
GitHub: <https://github.com/replikativ/rechentafel>
Feedback / issues / Excel quirks you've hit very welcome.
congrats on the release. The library can only read excel files but can’t write them, is that correct?
It can also write them through Apache POI. Its focus is on the interpreter so far, not storage formats.
that’s what we currently use (through docjure)
Cool! Maybe it would be also reasonable to implement the file format reading and writing in Clojure/Script, I mainly saw that POI does not have comprehensive Excel support and that it would be better to have a cross-platform implementation of the semantics with persistent memory support.
https://github.com/clj-holmes/clj-watson https://github.com/clj-holmes/clj-watson/releases/tag/v6.1.0 -- A Clojure tool that checks for vulnerable dependencies
Major release, with a huge amount of work done by @lee -- thank you! Updated to DependencyCheck 12.2.2.
• Check dependencies via classpath (separately from deps.edn), inspired by https://github.com/clj-holmes/clj-watson/issues/152, as a way to use clj-watson in contexts where deps.edn is not available or not the primary source of dependencies, e.g., Leiningen projects.
• Docs
◦ [OSS Index now requires credentials](README.md#oss-index-configuration), and has migrated to Sonatype Guide; see https://github.com/clj-holmes/clj-watson/issues/133
◦ Review and freshen docs
• Fix references to clj-watson.properties file in the documentation via PR https://github.com/clj-holmes/clj-watson/pull/130 from https://github.com/samumbach.
• Update DependencyCheck settings reporting https://github.com/clj-holmes/clj-watson/issues/141
◦ generalized occluding of sensitive values
◦ show properties filenames and counts of properties
◦ added unit tests
• Command line parsing review and fixes https://github.com/clj-holmes/clj-watson/issues/137
◦ -X/-T exec usage, as per convention, now throws on error instead of explicitly exiting process
◦ -X/-T exec usage now allows for conventional value syntax, e.g. :p '"deps.edn"' now works (in addition to :p deps.edn).
◦ -X/-T exec usage invalid boolean values now trigger an error (instead of being ignored)
◦ now warning when using dependency-check-only options with github-advisory database-strategy
◦ now reporting unrecognized options in appropriate style for -M vs -X/-T usage
◦ command line usage errors reviewed and updated for succinctness and clarity
◦ made code more REPL-friendly by only issuing System/exit from the outer edge of API
◦ added unit tests
• Switch to Maven as authority for version ordering https://github.com/clj-holmes/clj-watson/issues/146
• Output maps in reports using a familiar syntax https://github.com/clj-holmes/clj-watson/issues/147
• Bump deps https://github.com/clj-holmes/clj-watson/issues/134
• Add JDK25 to the CI test matrix https://github.com/clj-holmes/clj-watson/issues/143
Follow-up in #clj-watson