If you want the developer experience of using something that’s not Node, you can still get it from Deno.
But it turns out that few people care that much about purity, so it’s fortunate that they’re not relying on that.
If you want the developer experience of using something that’s not Node, you can still get it from Deno.
But it turns out that few people care that much about purity, so it’s fortunate that they’re not relying on that.
The answer is obvious in the programming language case: for those who do not want async, the addition of async/await begins to poison the ecosystem. Now they have a growing list of libraries that they cannot use if they want to avoid async, so the effort involved in picking a library goes up and the odds get increasingly high that they're locked out of some of the key tools in the ecosystem because new libraries without async become harder and harder to find.
For those who really hate colored functions, the addition of async is the removal of a feature: colorless functions are replaced with colored functions.
The same can be said of NPM compatibility. Sure, I can try to avoid it and stick to Deno imports and inspect each library that I use for NPM dependencies. But it gets harder and harder as time goes on, because a key feature of Deno has been removed: it's no longer an ecosystem reset.
So it reminds me more of trying to avoid CGO in Go or avoid “unsafe” in Rust.
It would be worse if Node-specific types started appearing as function parameters or return types, but that seems fairly rare even in npm libraries, so it seems easy to avoid.
Node API yes, NPM library no. If you add a dependency on a library that uses NPM you now depend on an entire web of transitive NPM dependencies, with all of the problems that entails. People don't dislike NPM because it's aesthetically displeasing—you can't just abstract away the problems with NPM. The ecosystem causes real problems with real software, and Deno initially recognized those real problems and set out to reset the ecosystem.
The only way in which NPM-compat is different than colored functions is that there's no static compiler feature telling you when you've added a dependency on a bunch of NPM libraries.
Though, it is nicer if it’s on jsr.io because you’ll see Typescript source code in the debugger.
There’s nothing about starting over that prevents ending up with a whole new rat’s nest of dependencies, if you’re not careful.
If you're gonna argue that fragmentation is a problem in the node ecosystem (which I agree with), you can't convince me that a plethora of approaches to asynchronous code is preferable to async/await and promises.
1) The original essay that coined this term was looking at it from a language design perspective. The argument is a fair one if that design question is up for debate, but that isn't the case for Javascript.
Of course, in (browser-compatible) Javascript, some things can not be done synchronously, but that's not up for debate.
(In general, I do agree that "you don't have to use it" is not a strong argument.)
Also, promises already color functions just like callbacks do. Async/await just changes the syntax by which that coloring is expressed. The real problem people have with async is that they prefer green threads as a solution to concurrency, not that they don't like the syntax.
The problem is that if you think statically, you can say "oh, just use the 'clean' subset". But the world is not static. If you think dynamically, you can see the full Node ecosystem as a fairly powerful attractor; why would I write a deno library that only works on deno when I can write a node library that works on both? Well, if I'm writing in the Node ecosystem, why not use the whole thing?
This is a general effect; it is very hard for people to set up long-term ecosystems that are "too close" to existing ecosystems. Generally the new thing will either get pulled in (as in this case) or ignored (as in the many cases of 'hey Typescript seems to have worked pretty well, let me do a Typescript-like gloss of this other language', which generally just get ignored). There are successes, like Typescript (JS is in general a special case because being the only language in the browser for so long it was both a language and a compile target; most other attempts to "Typescriptify" a language flounder on the fact that few if any other languages have that situation), Elixir (managed to avoid just being absorbed by Erlang, IMHO kind of a similar situation where the 'base langauge' for the ecosystem was not really the best), and the occasional Lisp variant that bubbles up (though like Clojure, usually with a story about where it can be run), but in general this is very hard to pull off, harder in some ways than simply starting a brand new language ecosystem, which is itself no picnic.
In Rust, you can call async functions from normal ones by spawning them on the executor. The .await syntax isn't as painful as dealing with callbacks and closures in JavaScript. Plus, if you call an async function incorrectly, Rust's compiler will catch it and give you a clear error message, unlike JavaScript, where bad function calls could lead to unpredictable behavior. The premises of the article don't apply, so Rust's async/await doesn't introduce the same "colored function" issues.
(See also https://without.boats/blog/let-futures-be-futures/ )
JavaScript itself has come a long way towards making coloring less painful. TypeScript+ESLint solves the weird unpredictable behavior issues with JS and async/await solves the syntax issue. Promises in general give well-defined semantics to calling an async function from a sync function. But all that only undoes some of the arguments about function coloring, not all of them. Fundamentally the same question applies: do you make async-ness part of the type system or do you instead build a system like green threads that doesn't put it in the type system?
I happen to think that coloring functions according to their async-ness is actually the right move (with the right ergonomic improvements), but plenty of people don't agree with me there even with all the ergonomic improvements Rust and TypeScript have made to the model.
It’s software you are mostly using to build end user software. Gauge it by how well it works and serves the end user.
This is why AI is good. It will just force this SWE vintage to solve the problem at hand instead of over engineering or holding on to some traditionalist views of the language.
Because you are losing something: a better ecosystem. Standardizing around… standards is a good thing. When I dive into the dependencies of any given Node app it’s a mess of transpiled code, sometimes minified even, there’s no guarantee what API it’ll be using and whether it’ll run outside Node (is it using the W3C streams API or the Node streams API?). But inertia is a powerful force. People will just use what’s there if they can. So the ecosystem never gets made.
> But it turns out that few people care that much about purity, so it’s fortunate that they’re not relying on that.
By that logic we never build anything newer or better. Python 3 is better than Python 2 and sets the language up for a better future. Transitioning between the two was absolutely torturous and if they just prioritised popularity it would never have happened.
I’m looking forward to whatever they’re going to do instead of KV, which I tried and is too limited, even for a KV store. (64k values are too small.) Something like Cloudflare’s Durable Objects might be nice.
You can’t “force” maintainers of old libraries to do anything. Deno never had that power. For people who are interested in supporting multiple alternate platforms, jsr.io seems pretty nice?
> You can’t “force” maintainers of old libraries to do anything. Deno never had that power. For people who are interested in supporting multiple alternate platforms, jsr.io seems pretty nice?
If enough people find Deno useful enough to skip some old libraries, maintainers are "forced", even thought Deno is not forcing anyone. If they do not adapt, then someone will just create a new library with better practices. In both cases there is pressure for better JS/TS evolution.
Because they are losing something.
All the time and money they are investing into node compat could have been used towards a Deno first party ecosystem. It's not like they have hundreds millions to spare. Deno is a small company with limited resources.
People kept complaining that they couldn't use Deno with NPM packages so Deno ended up focusing in providing faster horses.
In practice, it would stay a small group of hobbyists responsible for doing weird things like maintaining ports of npm libraries that deno users want, like official SDKs.
It’s a grim end state if you’re trying to be more than that. And it seems to only appeal to people with a weird vendetta against Node/NPM which is also a crappy user to cater to.
At least that's the theory. To be honest I don't see Deno's value add. The runtime is like... I mean node works fine at this point? And the capabilities system is both too pedantic and too simplistic, so it's not actually useful.
I don't understand the value add of Bun much either. "Native" Typescript support but at the end of the day I need a bundler that does more than what these tools tend to offer.
Now if one of these basically had "esbuild but built in"....
Although now that Node itself has basic TS support with type-stripping, this substantially improves matter. But that's a fairly recent thing, both Deno and Bun predate it by a long time.
Also Bun has a built-in bundler? I'm not sure how it compares with esbuild tho.
And for build scripts, it's not a usable approach because those are the ones that run the bundler. Now, one can argue that you shouldn't really need static typing for build scripts... and yet I have found valid bugs when switching them from JS to TS in the past.
Anyway, Node is rapidly improving its TS integration story, which is probably translating to less interest in Deno (and Bun). Although in case of Bun, the appeal of having a single coherent tool that covers building, testing, and publishing is very enticing after spending a few years in the Node ecosystem duck taping together various third party solutions to all of these.
I think my complaint is less that these runtimes do nothing, but more that I would expect them to do _even more_. But I'm not trying to make things much better.