Deleted Comment
Edit: I stand corrected. Ads were added later, but when first introduced they were clearly marked. I got my history wrong.
I'm interested in knowing whether there's something intrinsic to Go that encourages such a culture.
IMO, it might be due to the fact that Go mod came rather late in the game, while NPM was introduced near the beginning of NodeJS. But it might be more related to Go's target audience being more low-level, where such tools are less ubiquitous?
It's an entirely clean slate design, and I spent years taking my time on the core planning out the design; it's as close to perfect as I can make it.
The only things I can think of that I would change or add given unlimited time and budget: - It should be written in Rust, and even better a Rust + dependent types (which I suspect could be done with proc macros) for formal verification. And cap'n proto for on disk data structures (which still needs Rust improvements to be as ergonomic as it should be) would also be a really nice improvement.
- More hardening; the only other thing we're lacking is comprehensive fault injection testing of on disk errors. It's sufficiently battle hardened that it's not a major gap, but it really should happen at some point.
- There's more work to be done in bitrot prevention: data checksums really need to be plumbed all the way into the pagecache
I'm sure we'll keep discovering new small ways to harden, but nothing huge at this point.
Some highlights: - It has more defense in depth than any filesystem I know of. It's as close to impossible to have unrecoverable data loss as I think can really be done in a practical production filesystem - short of going full immutable/append only.
- Closest realization of "filesystem as a database" that I know of
- IO path options (replication level, compression, etc.) can be set on a per file or directory basis: I'm midway through a project extending this to do some really cool stuff, basically data management is purely declarative.
- Erasure coding is much more performant than ZFS's
- Data layout is fully dynamic, meaning you can add/remove devices at will, it just does the right thing - meaning smoother device management than ZFS
- The way the repair code works, and tracking of errors we've seen - fantastic for debugability
- Debugability and introspection are second to none: long bug hunts really aren't a thing in bcachefs development because you can just see anything the system is doing
There's still lots of work to do before we're fully at parity with ZFS. Over the next year or two I should be finishing erasure coding, online fsck, failure domains, lots more management stuff... there will always be more cool projects just over the horizon
From the system card:
"In the near future, we plan to integrate these capabilities into a single model."
The idea is that, yes, the compiler will infer whether or not a function is async (in the stackless async sense) based on whether it has any "suspension point", where a suspension point is either: * Usage of `@asyncSuspend` * A call to another async function
Calls through function pointers (where we typically wouldn't know what we're calling, and hence don't know whether or not it's async!) are handled by a new language feature which has already been accepted; see a comment I left a moment ago [1] for details on that.
If the compiler infers a function to be async, it will lower it differently; with each suspension point becoming a boundary where any stack-local state is saved to the async frame, as well as an integer indicating where we are in the function, and we jump to different code to be resumed once it finishes. The details of this depend on specifics of the proposal (which I'm planning to change soon) and sometimes melt my brain a little, so I'll leave them unexplained for now, but can probably elaborate on them in the issue thread at some point.
Of course, this analysis of whether a function is async is a little bit awkward, because it is a whole-program analysis; a change in a leaf function in a little file in a random helper module could introduce asynchronocity which propagates all the way up to your `pub fn main`. As such, we'll probably have different strategies for this inference in the compiler depending on the release mode:
* In Debug mode, it may be a reasonable strategy to just assume that (almost) all functions are asynchronous (it's safe to lower a synchronous function as asynchronous, just not vice versa). The overhead introduced by the async lowering will probably be fairly minimal in the context of a Debug build, and this will speed up build times by allowing functions to be sent straight to the code generator (like they are today) without having to wait for other functions to be analyzed (and without potentially having to codegen again later if we "guessed wrong").
* In Release[Fast,Small,Safe] mode, we might hold back code generation until we know for sure, based on the parts of the call graph we have analyzed, whether or not a function is async. Vtables might be a bit of a problem here, since we don't know for sure that a vtable call is not async until we've finished literally all semantic analysis. Perhaps we'll make a guess about whether such functions are async and re-do codegen later if that guess was wrong. Or, in the worst case... perhaps we'll literally just defer all codegen until semantic analysis completes! After all, it's a release build, so you're going to be waiting a while for optimizations anyway; you won't mind an extra couple of seconds on delayed codegen.
If this doesn't make the argument that Zig has certainly not defeated function coloring, I don't know what would.
The fact that this change to how my program runs is hidden away in the compiler instead of somewhere visible is not an improvement.