Bun v0.8
bun.sh
bun.sh
Until recently I've been using Deno (mostly to avoid using Node and the tooling hell that entails) and it looks like for my use-cases Bun is getting there. I've had a pleasant experience using Bun as the basis of a test harness.
Here's my question (with a tiny bit of lead-in):
What I like about Deno is the integrated LSP (reducing tooling hell), are there any plans for Bun to feature this too? Bun already internally transpiles TypeScript which is great but having the LSP bundled too would give this single binary integrated experience a boon I feel.
Looking forward to Bun 1.0!
P.S. I'm starting to stretch my Zig muscles, you looking for Zig developers? ;)
How <<isn't>> the previous comment relevant?!?
By this definition, is there any company that doesn't have work / life balance? By this new definition you propose, does the term mean anything?
Is 996 a good work life balance because "it is what some people want"?
I've never seen anyone that could sustain an 80+ hour per week grind and make it out without severe personal issues (whether they are willing to acknowledge it or not). I've seen many, many incredibly talented people burn out and suffer permanent health or career damage to hit their short-term goals. I personally know an otherwise healthy 30 year old swe who had a stress related heart attack. It may be what some people want but you can't grind your way out of being a human.
Then there is the not speaking out, resulting in: 1) Burn out and quit. 2) Company dumps or fires them after burning them out. Then does the same to the new ones. Until something obvious or tragic stops them. 3) Quiet destruction of personal lives. Sometimes leading to significant health and/or mental problems, related to stress, and even suicide in some cases.
Balance is necessary, because otherwise it can be like playing with fire. It's all "fun and games", until people get or realized they got burned.
The people who accepted job offers self selected for having a passion for pushing technology forward.
I tried to keep things as sane as I could, but I'd have to go in on weekends and usher people out of the office.
For some people, building cutting edge things is /fun/.
If you have a lot of duplicated functionality in a web frontend and backend, it may be a lot more maintainable to do the development once and not have to keep two implementations in sync.
As far as performance, if you’re using one of the JS application frameworks with server-side pre-rendering time to interactivity may very well be faster than anything you can build in Go or Rust.
I mean unless you have pure front end app - you are still going to talk to some backend. Regardless of how you got your frontend part - generated by the server or a static file served by nginx.
For Rust, Leptos (https://leptos.dev/) would be one choice that can do SSR+hydration (but not intermixed with server-side components, at least AFAIK not yet)
Why? Because js and java are JIT compiled for the very specific cpu they run on, and HotSpot is basically PGO but running all the time.
Granted, this is if and only if the js or java is carefully and properly written, which never happens...
They were already using NodeJs or would have. And that's a large part of the current ecosystem.
That's why outsourcing firms (both inside and outside the US) love JS backend.
The fragility of JS codebase shows much later.
I'm going to do some experiments in the next few days and see how it goes.
Roughly, the way we're thinking of adding Windows support to Bun is:
1) Get all the Zig code using platform-specific system APIs to use the closest Windows equivalent API. Fortunately, we have lots of code for handling UTF-16 strings (since that's what JS uses in some cases)
2) Get uSockets/uWebSockets (C/C++ library we use for tcp & http serve) to compile for Windows, or fall back to using libuv if it takes too long to make it work
3) Get the rest of the dependencies to compile on Windows
4) Fix bugs and perf issues
There are a lot of open questions though. None of us are super familiar with I/O on Windows. JavaScriptCore doesn't have WebAssembly enabled on Windows yet.
The biggest thing I'm worried about (other than time) re: Windows is async i/o. In Bun, we _mostly_ use synchronous I/O. Synchronous I/O is simpler and when using SSDs, is often meaningfully lower overhead than the work necessary to make it async. I've heard that anti-virus software will often block I/O for potentially seconds, which means that using sync I/O at all is a super bad idea for performance in Windows (if this is true). If that is true, then making it fast will be difficult in cases where we need to do lots of filesystem lookups (like module resolution)
On Linux, not closing file descriptors makes opening new ones on multiple threads occasionally lock for 30ms or more. Early versions of `bun build` were something like 5x slower on Linux compared to macOS until we narrowed down that the bug was caused by not closing file descriptors.
You may want to take a look on https://youtu.be/qbKGw8MQ0i8?si=HO5b0MuljPQN9Sb2
Disclaimer: I work at Microsoft and ship code to Windows, but the above are just my personal opinions.
I got JavaScriptCore compiling with WebAssembly enabled yesterday, but I don't know how long it'll take to get it to actually work.
The bigger problem for Bun is that JavaScriptCore doesn't have the FTL JIT enabled on Windows [1]. It's going to be much slower than other platforms without that final tier of JIT, shows up pretty dramatically on benchmarks.
Some of apps we use will simply cease to function with any AV scanning in place.
If there are the resources a community would self-host a decentralized, federated server where users are in control of their account/data along with the community in control of moderation, bans, CoC, ToS… & then bridge to other services from that base if they are seen as useful. If a community has less resources certain servers use less resources—especially if the server isn’t supposed to hold the entire history (which these chat rooms shouldn’t be seen as a place for permanent decision making anyhow).
> The plan is to run our own servers on the edge in datacenters around the world. Oven will leverage end-to-end integration of the entire JavaScript stack (down to the hardware) to make new things possible.
So how does oven-sh the company make money? It sounds like you release Bun open source, and then sell access to your edge infrastructure to enterprises?
Is these edge servers basically a running a smarter version of NPM with a CDN? Can you say more about what this may eventually do?
Will individuals be able to use the edge servers via some free tier?
Does Bun in it's current form use this edge infrastructure already?
It was eye-opening, in terms of how often us JS programmers play fast & loose with "a little filter" here, "a little map" there, and end up with death by a thousand allocations.
Given that context, I'm curious:
1) How much you think Bun's (super-appreciated!) fanatical OCD about optimizing everything "up to & outside of JSC" will translate to the post-boot performance of everyday backend apps, and
2) If you're tempted/could be compelled :-D to create a "Mojo of TypeScript", where you do a collab with Anders Hejlsberg to create some sort of "TypeScript-ish" language that, if a TS programmer plays by a stricter set of rules + relies on the latest borrowing inference magic, we could bring some of the Bun amazing-performance ethos to the current idiomatic FP/JS/TS style that is "lol allocations everywhere". :-)
Or, maybe with Bun bringing the right native/HTTP/etc libraries wrapped around the core runtime, doing "just the business logic" in JS really won't be that bad? Which iirc was the theory/assertion of just-js when its author was benchmark hacking Techempower, and got pretty far with that approach.
Anyway, thanks for Bun! We're not running on it yet, but it's on the todo list. :-)
Has this been added? That PR got closed right? Whilst valid, it was sad that Bun didn't make it. Would be good if someone from the community or Bun team can give it another go.
The paper's language is a bit different than contemporary (2023) language.
`map()` is called `map-fn`.
`reduce()` a.k.a. `fold` seems to be `collect-fn`, although `collecting-fn` also seems interesting.
sorting, uniqueness and permutation seem to be covered by `producing`.
Just think of McIlroy's famous pipeline in response to Donald Knuth's trie implementation[mcilroy-source]:
tr -cs A-Za-z '\n' |
tr A-Z a-z |
sort |
uniq -c |
sort -rn |
sed ${1}q
As far as pipeline or stream processing diagrams are concerned, the diagram on page 13 (document page 15) of Waters(1989a) may also be worth a closer look.What the SERIES compiler does is pipeline the loops. Think of a UNIX shell pipeline. Think of streaming results. Waters calls this pre-order processing. This also seems to be where Rich Hickey got the term "transducer" from. In short it means dropping unnecessary intermediate list or array allocations.
Shameless self-plug: Eliminated unnecessary allocations in my JavaScript code by adding support for SERIES to the PARENSCRIPT Common Lisp to JavaScript compiler. The trick was (1) to define (series-expand ...) on series expressions so that they can be passed into (parenscript:ps ...) and (2) the parenscript compiler was missing (tagbody ... (go ...) ...) support. The latter is surprisingly tricky to implement. See dapperdrake(2023). Apologies for the less than perfect blog post. Got busy actually using this tool. Suddenly stream processing is easy, and maintainable.
When adding a Hylang-style threading macro (-> ...) you get UNIX style pipelines without unnecessary allocations. It looks similar to this:
(-> (it :let*-symbol series::let)
(scan-file in-path-name #'read)
(map-fn T #'some-function it)
(collect 'list it))
Sadly, the SERIES compiler available on quicklisp right now is a
bit arcane to use. It seems like it may have been more user friendly
if it would have been integrated into the ANSI Common Lisp 1995 standard
so that is has access to compiler internals. The trick seems to be to
use macros instead of (series::defun ...) and use (series::let ...) instead of (cl:let ...). Note, that the two crucial symbols 'defun and 'let
are not exported by SERIES. So using the package is insufficient
and pipelining fails without a decent warning.Am chewing on the SERIES source code. It is available on sourceforge. [series-source]. If anybody is interested in porting it, then please reach out. It seems to be of similar importance as Google's V8 relooper algorithm [relooper-reference]. Waters(1989b), page 27 (document page 29) even demonstrates an implementation for Pascal. So it is possible.
References:
dapperdrake(2023): Faster Loops in JavaScript https://dapperdrake.neocities.org/faster-loops-javascript
Waters(1989a) document page 48, paper page 46 https://dspace.mit.edu/bitstream/handle/1721.1/6035/AIM-1082...
Waters(1989b) document page 29, paper page 27 https://dspace.mit.edu/bitstream/handle/1721.1/6031/AIM-1083...
[relooper-reference] http://troubles.md/why-do-we-need-the-relooper-algorithm-aga...
[series-source] https://series.sourceforge.net/
[mcilroy-source] https://franklinchen.com/blog/2011/12/08/revisiting-knuth-an...
For both getting function composition and avoiding unnecessary intermediate allocations, the naive approach to using the SERIES package is insufficient. And the error messages it returns along the way are unhelpful.
Evaluating (defpackage foo (:use :cl :series)) fails to import (series::defun ...) and (series::let ...) and (series::let* ...). So, when you think you are following the rules of the paper, you are invisibly not following the rules of the paper and get the appropriate warnings about pipelining being impossible. That seems somewhat confusing.
After reading the source code, it turns out the answer is calling (series::install :shadow T :macro T :implicit-map nil). How is (series::install ...) supposed to be discoverable with (describe (find-package :series)) in SBCL if it is an internal symbol of package SERIES (?) Usability here is somewhat less than discoverable. Listing all exported package symbols of SERIES obviously also fails here.
Furthermore, the source code and naming in "s-code.lisp" suggest that (series::process-top ...) may be useful for expanding series expressions to their pipelined (read optimized/streaming/lazy) implementations. This is desirable for passing the optimized version on to PARENSCRIPT or other compilers. Here is the catch: It fails when the series expression is supposed to return multiple values. One of the points of using SERIES, is that Waters and his fellow researchers already took care of handling multiple return values. (If the lisp implementation is smart enough, this seems to mean that these values are kept in registers during processing.) After some tinkering, there is a solution that also handles multiple return values:
(defun series-expand (series-expression)
"(series::process-top ...) has problems with multiple return values."
(let (series::*renames*
series::*env*)
(series::codify
(series::mergify
(series::graphify
series-expression)))))
Will submit pull requests once I am comfortable enough with the source code.Yes, the SERIES papers Waters(1989a,b) bemoan the lack of deep integration with Common Lisp compilers. And yes, they could have been resolved by making SERIES part of ANSI Common Lisp like LOOP was. They could theoretically also have been resolved by having explicit compiler and environment interfaces in ANSI Common Lisp. That is not the world we seem to live in today. Nevertheless, package SERIES solved all of the hard technical problems. When people know about the documentation failings, then SERIES is a powerful hammer for combining streaming/lazy-evaluation with function composition as well as other compilers like PARENSCRIPT.
Anyway, I guess series::let is an unexported symbol? I.e. not series:let? There is a good reason for that.
If series:let were exported, then you would get a clash condition by doing (:use :cl :series). The CL package system detects and flags ambiguous situations in which different symbols would become visible under the same name. You would need to a shadowing import for all the clashing symbols.
It's probably a bad idea for any package to export symbols that have the same names as CL symbols. Other people say that using :use (other than for the CL package) is a bad idea. Either way, if you have clashing symbols, whether exported or not, you're going to be importing them individually if you also use CL, which is often the case.
That's because speed isn't always top priority. Readability is very high on the list.
I would rather have a slightly slower .map or .filter in a chain than a harder to read nested for or while loop.
I often find map/reduce/filter easier to read when using named functions (or lazy data structures ) for intermediary results - depending on the language/runtime that might imply more allocations - or not.
Eg pseudocode:
Integers.filter(//non-obvious prime sieve).sum()
Vs
Primes = Integer.filter(//non-obvious prime sieve)
Primes.sum()
Or lifting the anonymous filter to primes()-filter:
Integers.primes().sum()
But I have seen "functional contraptions of horror" where those functions are both chained and nested which were completely undecipherable by mere humans.
And at least from my personal impression, people who are a fan of this type of functional style are also more likely to create such horrors (which they themselves of course find totally readable and superior to "unreadable" loops) - I suspect that there's often a bit of cargo-culting going on.
You can make a similar function that changes the data in place and will be faster. But that comes with side effects which is not functional style.
In the front-end we were making about 20 API calls to fetch data we probably don't need yet and the developer is like: the problem has to exist in the way we call them, time to optimise the loops!
Lodash is MUCH faster than native because it uses iterators so you aren't actually looping over and over.
We need builtin iterator versions of all these looped functions so it adds an implicit/explicit `.valueOf()` that calls the chain and allocates only one new array.
But it's not going to help all that much with this problem. The iterator protocol in JS involves an allocation for every individual value, and while in some cases that can be optimized out, it's pretty tricky to completely optimize.
It's always going to be difficult to beat a c-style for loop for performance.
Bun is extremely fast at data processing tasks. Shuffling data from one place to another (disk, network, APIs, etc). We use SIMD, minimize allocations/copies and pay lots of attention to what system calls are used and how often and where. A naively implemented script in Bun that moves data using Bun’s APIs will often outperform a naively implemented program written in Rust or Go. Bun’s APIs try really hard to make the obvious & default way also the fast way.
That, and Bun’s builtin build tooling are where Bun’s performance shines.
> “Mojo of TypeScript”
I’ve thought a little about this. I think it’s a project that’d take 5+ years and crazy hard to hire the right people to do it. I think it’s mostly unnecessary though. JITs are really good. The API design is usually the reason why things aren’t as fast as they could be.
Sounds exciting, do you have some example benchmarks I can run that show this?
What are the major difficulties you see? Is this estimate for supporting all existing TS code...or as the OC said, a new language with only newly written code.
The way I naively think about it is to imagine transpiling TypeScript code to Zig code. How far could that take you?
And if you restricted how much dynamic-y stuff you could do...maybe with a linter. I always get the feeling that 90% of the business logic (sequence, selection, iteration) is that same between languages whether they are interpreted or compiled, with just some memory management stuff added on top - which can be abstracted anyway.
Nice! That makes a lot of sense, and look forward to trying them.
Fwiw I sometimes worry about the slippery slope to infra that exists on the JS side, i.e. I work a lot in a GraphQL backend, and even if Bun gave us (or really our framework, so fastify/mercurius) a super-optimized way of getting the raw bytes off the wire, Mercurius is still doing GraphQL parsing, validation, routing, response building in JS land.
Granted, I want to keep my application's business logic in TS, but naively it seems tempting to push as much of the "web framework" / "graphql framework" as possible into the native side of things, where as I think historically Node/etc API have stopped at the "here's the raw HTTP request off the wire".
> I’ve thought a little about this.
Sweet! That's awesome just to hear that it's crossed your mind. Agreed it would be a moonshot. And, yeah, I'm perfectly happy leaning into JITs and good APIs.
Thanks for the reply!
This is basically just rust, no? As a TS developer, I've found picking up rust to be really neat.
Dunet is really good.
https://github.com/mcintyre321/OneOf
https://github.com/domn1995/dunet
It's on the roadmap and will arrive at some point:
https://github.com/dotnet/csharplang/blob/main/proposals/dis...
That was certainly the promise/hype of Rust ~2-3 years ago, that it was going to become so ergonomic that even "boring line-of-business applications" (i.e. JS backends) could be written in Rust, by everyday programmers, without any slow down in delivery/velocity due to the language complexity.
But, from what I've seen, that's not played out, and instead the community is still on the look out for the killer "systems-language performance, but scripting-language ergonomics" language.
https://zackoverflow.dev/writing/unsafe-rust-vs-zig/
Zig is also very easy to learn in comparison to rust.
For example Erlang (and Elixir) has Native Implemented Functions[0] (NIF) where you can call native code directly from Erlang. Elixir has the zigler[1] project where you can call Zig code directly from Elixir.
Maybe you can see where I'm going with this, but it would be super cool to have the ability to call Javascript code from within Elixir. Especially when it comes to code that should be called on the server and client. I'm the developer of LiveSvelte[2] where we use Node to do SSR but it's quite slow atm, and would be very cool to use Bun for something like this.
In any case Bun is super impressive, keep it up!
[0] https://www.erlang.org/doc/tutorial/nif.html
What is your view on the usefulness of sandboxing and why it was skipped for bun?
"Rather than runtime permission checks (Deno's model) which could potentially have bugs that lead to permissions being ignored, Bun plans to have binary dead code elimination based on statically analyzing what features/native modules are used by the code."
@Jarred was super responsive to help me adapt bun to our distributed cache storage, so all props to him.
I'm looking to investigate using Windmill as a website builder for some small internal system. Few questions: - Is it possible to setup custom path for flows (to hack it into a REST API) - How can we go and make authenticated flow
Is the plan to eventually build a cohesive web framework kind of like Rails/Laravel?
$ bun x create-next-app What is your project named? … mytest2023 Would you like to use TypeScript? … No / Yes Would you like to use ESLint? … No / Yes ? Would you like to use Tailwind CSS? › No / Yes
I let it sit there for 30 minutes. No go.
> This domain debug.bun.sh hosts a stripped-down version of Safari Developer Tools designed for debugging Bun. Open the link in your preferred browser and a new debugging session will start automatically.
Is there a way debug without an Internet connection?
More seriously, you can usually run a debugger locally - it's unclear if that's true for the interface for bun? Eg: can I host the stub on localhost?
Useful when travelling, or debugging in a locked down environment, like a private cloud/data center?
Wish there was a musl version.
But here we are.
10 years ago, I thought for sure that JavaScript devs were eventually going to get sick of breaking and deprecating changes, but they’re still going strong with picking frameworks that just don’t give a shit, switching to new breaking tool chains, etc.
It seems that there are A LOT of developers willing to put up with a whole lot more than I am.
But I'm not sure Bun should go to 1.0 without Zig going to 1.0 too. And Zig 1.0 is a distant target, which is a normal timeline for a programming language.
Andrew has made the claim a couple years ago that Zig should not be used in production yet. The part about security is not at all part of anything he ever claimed, and is in fact only something that you went crazy about on your own.
See: https://news.ycombinator.com/item?id=34669045
While I can understand holding real hard onto your opinion, please don't put words in people's mouths, especially when the person in question does not share your position on the matter at all.
- he says it shouldn't be used in prod
- he acknowledges there are potential security flaws that should be revisited before 1.0 (https://github.com/ziglang/zig/pull/4929)
I just feel like there is a lot of hair splitting here.
https://news.ycombinator.com/item?id=34675866 (Feb 2023)
https://news.ycombinator.com/item?id=34675665 (Feb 2023)
https://news.ycombinator.com/item?id=34669045 (Feb 2023)
https://news.ycombinator.com/item?id=34439732 (Jan 2023)
https://news.ycombinator.com/item?id=34138064 (Dec 2022)
We detached this subthread from https://news.ycombinator.com/item?id=37246288.
Note that I don’t really think that bun is the solution for everything or really that it is all that great. I am just pointing out that you are trying to compare two things that are not really the same thing at all. One is probably a strict superset of the other
Bun also supports Vite.
No we don't. Bun is not Vite.
Bun the package manager replaces npm/yarn/etc
Bun the bundler replaces esbuild/swc/etc
Bun the runtime replaces Deno/node/etc
Did Jarred think that having a weaker variant of every feature in the Javascript world would somehow make it more attractive? Why was "bun build ..." considered better or necessary when there is `bunx esbuild ...`
https://github.com/oven-sh/bun/issues/159
I like that it's trying to be a faster Node, and the better native database drivers etc. Having a decent package manager alongside is also nice but anything else makes me question the long term vision/feasibility of Oven and Bun's maintenance.
Once you get it, you will realize how much of a game-changer it is. This is the reason Bun is going to win.
I'd rather install Bun, then get on with building our app.