De‐bloating Javascript
30 points by Claudius
30 points by Claudius
De-bloat your javascript by... adding an opaque 3.3mb WASM blob and then writing your app in a lisp?
I can get quite a bit done with vanilla JS and zero extra dependencies before I hit even a tenth of that size.
Like I suggested elsewhere, it's worth not dismissing this from this one small section in the docs. While I don't sympathize with this use case, this is a fun Lisp passion project with the kind of oddities you just don't see enough of these days. And refreshingly not vibe-coded, every single piece of documentation is quirky and clearly written by a human. Love it.
this is a fun Lisp passion project
Except that it's under the org of the equivalent of Google in Korea.
I guess you're implying that people should be aware of that if they wanted to take this on as some kind of dependency? Otherwise I don't understand how that has any impact on the fun or passion part.
No I'm implying (well, guessing) it's not a fun/passion project because most people don't put fun/passion projects under the corporate github account.
Oooh. Claude Roux is actually at Naver Research Europe. I can't find it right now but from what I understand he was previously in the NLP space at Xerox Research, but that got taken over by LLMs and he pivoted his interests into PL. This feels more like a research lab effort to me so I think that doesn't rule out fun / passion. They have examples of programs written in Greek, this can't be all serious :)
swannodette's guesses are pretty spot on. I still work at NLE (Naver Labs Europe). Our lab was previously XRCE (Xerox Research Centre Europe), and I worked for 30 years on Symbolic AI. I implemented a parser based on my PhD thesis called XIP (Xerox Incremental Parser), which was used for 20 years in my lab and is still used in a few labs in Europe. We won an NLP competition as late as 2016, in Sentiment Analysis with this tool (SemEval 2016). My expertise is mostly in Formal Grammar algorithms, and I've implemented my fair share of pattern matching algorithms over the years, with a lot of work on automata, lexical transducers and first order engines, there is still a linguistic transducer available in LispE. XIP was a parser that could run at 3,000 words per second on 2007 hardware. Lauri Karttunen was one of my colleagues around 2000, and he was the world expert on lexical transducers at that time. I also implemented a full Prolog engine as part of another language I developed, Tamgu. To answer the corporate-account question: LispE is hosted under /naver/ because I work there and Naver gave the go to share it as an Open Source project. In a way, LispE is the embodiment of all my research over the many years of my career. My research domain has now moved to agentic harnesses based on LispE running in the browser, somewhat unexpectedly: formal grammars and predicates proved to be a very efficient way to handle and control agents. Prolog to the rescue!!! I will come up with some presentation about this work very soon. LispE has many features directly derived from my research on automata and transducers. I put a lot of importance on speed and memory efficiency, because I started with computers that had less memory than a microwave oven. I know Lisp is not the most popular language today, but having spent a long time on compiling and parsing, I think this is the wrong way to talk about it: Lisp is fundamentally a language that writes everything down as an AST, and as such, any other language can be projected as a Lisp program. And that is a superpower that I really love.
But the worst part is that the language itself is pretty lacking when you need string or calculus functions. You then download a ton of libraries to do stuff that is usually part of any basic programming language.
I am always taking suggestions for new things to add to the standard library! Recent additions include set union/intersection/etc, sum, base64.
That said I basically never hear requests for calculus functions, and the only string related functions people regularly request are .reverse (which doesn't have many convincing non-toy-problem use cases) or .titleCase (which is doable, and discussion is ongoing, but it requires internationalization data), neither of which are in this library.
I think a lot of people don’t know anyone in tc39 and don’t realize that they’re in our midst, or feel that it’s not worth trying because it’d be 3 years before their suggestion lands anyway and a utils/ folder sucks but can happen right now.
There's also a forum and a matrix chat room! Not really fonts of great ideas but they're linked in our docs and delegates do read both, especially the matrix room.
To be honest 3 years is if anything optimistic. But I plan to be programming in ten years (even if the bots are writing all commercial code at that point, it's still fun for me), so I still think it's worth trying to improve things.
In a way it's a little pessimistic because there are still benefits to having a thing in the current or next draft ecmascript standard even if they aren't in the interpreters yet. It means that it's safe to start to polyfill them or emulate them with a compiler transform.
Thanks... the stdlib has moved more than I tracked, I should revise the article a bit. My argument is actually less about what JS lacks in terms of function and more about the delivery model: even with a fully-stocked stdlib, the average app still ships megabytes of transitive npm dependencies. LispE-as-WASM (3.3MB, ~450 documented functions, C++ core, source on GitHub) is an experiment in what an auditable runtime looks like. We've actually built an agent harness on top of it, LispE rules executing on the fly... In the browser itself. I'm a big fan of the JS event loop!!!
"Megabytes of transitive NPM dependencies" is quite misleading. It's like calculating all of the transitive dependencies required to compile a C++ binary as part of the final binary. Our work project which is not at all optimised for size is ~700kB of JS in production. Looking at another (much larger) project that I've previously worked on, their bundled JS is ~800kB of which ~150kB is GTM. On most sites the bulk of the size is actually images (and they often also change much more frequently than the code).
I think most sites could (and should) try to make their sites more lightweight but it's just not a priority for the business.
Modern JS code and libraries tree-shake quite well. There are also way fewer browser hacks and polyfills required nowadays (that would bloat the final size).
Modern JS code and libraries tree-shake quite well.
I've been seeing a trend towards libraries like date-fns that expose a large number of free functions and away from libraries like moment that expose classes with large numbers of methods, specifically because free functions tree-shake much better in JavaScript. A bundler can easily see whether or not your code contains any references to a particular function, but it can't reliably tell whether or not you ever call a particular method on a class if you use that class anywhere, because you might call it with someObject["a" + "dynamically" + "generated" + "string"]() syntax.
Google Closure Compiler used to manage this problem by changing the semantics. It would rename all of the methods and functions in your program to short names if you ever used class Foo { someMethod() {} } or Foo.prototype.someMethod =function() {} syntax. It would only preserve the names at runtime if you explicitly used [] syntax for creating the methods like Foo.prototype['someMethod'] = function() {}.
No really... [JavaScript] syntax seems to have been invented by someone who wanted to bet that he could push more brackets in a code than C++ and Lisp together. [...] Who could come up with a syntax as bloated as this? You open a parenthesis, and within the parentheses you open a curly bracket, and you have to close all of them in the right order.
I've learned to get nervous when someone describes syntax as bloated. A minimal syntax can be much more difficult to scan than one that makes use of separate characters for visual distinction.
Let's see what LispE offers as an alternative:
(defun gol8(⍵) ((λ(⍺) (| (& (== ⍺ 4) r) (== ⍺ 3))) (⌿ '+ (⌿ '+ (° (λ (x ⍺) (⊖ ⍺ x)) '(1 0 -1) (↑ (λ (x) (⌽ ⍵ x)) '(1 0 -1)))))))
... !?
This a port of course of a famously terse APL example https://aplwiki.com/wiki/Conway's_Game_of_Life
Wow, i would not allow such in any production
I think we have not remotely milked what might be possible with more succinct syntax in appropriate domains. There's still a lot of juice to milk from APL's brevity. A great example is Nile/Gezira - this is an old Bret Victor visualization of it at work - https://tinlizzie.org/dbjr/high_contrast.html, click around to see the mathematical syntax at the bottom. Alan Kay's Viewpoints Research Group showed that you could get a whole graphics rendering stack include text layout with just 500 lines of code.
In my experience with APL-like languages, you get the succinctness by knowing the language super well and doing lots of "clever things". For example, the famous GoL APL example starts by creating 8 translated matrices and adding all of them to the original board. To write it you have to understand how rotate works, what it does in edge cases, how it interacts with negative numbers, how an array behaves under outer product, how an array table behaves under outer product, etc. In comparison, the same approach in an imperative language takes more lines of code, but you don't need as much language expertise to write them!
So you got a necessary tradeoff between how terse an expert can be and what you can solve without needing to be an expert. I think it's reasonable for production languages to emphasize the latter.
I've also found that the succinct solutions are highly optimized for the current problem, so are hard to change for new requirements. What happens if the GoL program needs custom rulestrings? What if you want to model Brian's Brain? It's easy to see how to adapt naive imperative code, a lot harder to adapt the APL code!
This is a weird entry point into this project, this seems better https://github.com/naver/lispe/wiki/1.-Introduction. It's a native Lisp project that offers WASM as a target. There's some fun geeky stuff in there for PL nerds.
My first impression was that this is a fairly contrived piece of JavaScript.
I suspect much of the hatred comes from early browser incompatibilities, painful DOM APIs, and syntax footguns -- things that have essentially zero impact on my day-to-day today.
Modern JavaScript (especially with TypeScript and sane lint rules) is perfectly fine as a language.
There are still issues -- CJS vs ESM, supply chain risk, ecosystem churn -- but most of those sit outside the language itself.
Who could come up with a syntax as bloated as this?
Oh yeah, balancing parentheses that is so annoying that people have built editor extensions for them, that's a great experience. If you want this style of programming (low syntax, functional), flip it around and go for something concatenative.
I'm curious as to how a lisp interpreter can reach 3.3 MEGAbytes, compiled. This is "de-bloating"?
It's most likely because it includes a large standard library. That said, I agree that shipping a large standard library with code you're not using is hardly de-bloating.
Who could come up with a syntax as bloated as this? You open a parenthesis, and within the parentheses you open a curly bracket, and you have to close all of them in the right order.
SHOCK! HORROR!
Amazing you have to close them in the right order!
If you want a language where you don't close them in "the right order" there's J, but the Lisp enthusiasts who are so horrified at the verbosity of a language like JavaScript often turn on their heel at APL-family languages and then start arguing about how clarity and readability are much more important than being maximally terse.
LispE's author clearly has at least some appreciation for APL primitives, but given that exposure to a language with concise notation I struggle to understand preferring the long, sparse, deeply-nested sexpr version of Conway's Life to its equivalent in APL, J, K, or even Q.
TBF even in J you have to close them in the right order if you don't want strict right-to-left precedence or want to use direct definitions (which are great).