Components are basically COM / CORBA for WASM. Just like COM enabled multiple languages to exchange structs and function calls with each other, WASM components do the same.
The C ABI approach works fine but becomes complicated when you want to pass things that aren't fixed-length, because unlike the C ABI usage when linking different libraries into the same address space, the host and the module run in different address spaces. Eg if the host wants to pass a string to a module fn, it first needs to call a different "malloc" module fn to allocate some space in the module address space and return that address, then the host writes the string to that address, then calls the original fn it wanted to call with that address, then calls a third "free" function at the end to release that allocation. Does that work? Of course it does. But the component ABI abstracts over all that for you and lets you call the function with the host's string type, and the runtime on the host side and bindings on the module side handle all the thunking so that it appears on the module side as the module's string type.
TFA doesn't seem to have any arguments other than "It's too many new words for me to learn so it's unnecessary I decided." Eg:
>And don't get me started on WIT's kebab-case identifiers (function names, interface names...). Why??? How can a specification about cross-language interoperability can come with a convention that basically no programming language use?
The point of the kebab case is that it does *not* show up in the generated bindings. It's processed by the bindings generator into the appropriate casing for the language. Eg a function name `foo-bar` becomes `foo_bar` in Rust, and a type named `foo-bar` becomes `FooBar`.
I think the argument here is that you don’t need a full N:1:M mapping of data types to make this work. Transparency is a myth. If you try it (as in Sun RPC, DCE, CORBA, COM, etc.) you end up just writing wrappers anyway as you hit some edge case that the universal type system doesn’t support. E.g., look at the clunky IDL interfaces COM had to invent to get interop with JavaScript objects because IDL had baked-in assumptions about how records work.
Since the point is to enable component-level interop, not fine-grained function interop, you can instead define a simple interchange format (more JSONish than IDLish) and write the library interfaces accordingly. More of a microservice approach than a linker approach.
Counter-point: I happen to be working at $dayjob on things related to Components (both on the host side and the module side), including using some "WIP" things like the "resource" feature (opaque handles with methods), and haven't hit any edge cases.
I just realized: The very fact that WASI isn’t using any of the previous N attempts at a supposedly universal IDL, but instead inventing yet another one, itself demonstrates the problem with this approach.
Or COM's evolution, WinRT, where the .NET metadata also doesn't really map to JavaScript or C++, without some additional kludges with metadata as workaround.
Don't forget the extra fun that happens when you call free() from a different allocator than the one that allocated the memory object, though I suspect WASM's module approach might make it difficult to happen...
Yes, that won't happen here, because the host is just calling some global malloc and free exported by the module. Well, obviously the implementations of those globals can be buggy in some way, but that's the module's bad.
> I strongly believe that async is the new billion dollar mistake of the 2020's: a design aberration that wasted so much developers time that it had cost billions of dollars to companies.
Async as a paradigm has probably created billions of dollars in that it's allowed many many many developers to reasonably scale beyond what they could've otherwise.
Agree wholesale. I've got a complex data reader built on a single server that receives tens of thousands of requests a day, and most tend to happen in a few hour window. Async was super important to scale to user requests by allowing non-blocking calls to not get captured behind blocking ones.
Just some background first: one of the billion dollar mistakes is the null pointer which is a special value to be assigned to any type. It was inherent in the first high (today considered low) level languages. Those languages (C most importantly, then Java and nowadays even Go) enabled many more programmers to create programs and they likely would not have been able to, had they been forced to write assembly. So, it's a billion dollar mistake within a trillion dollar revenue.
The async now solves a similar practical problem but in the space of prallelism (or concurrency, in some cases). A different approach might have given us the same result, but a different implementation that might be just as approachable.
Optionals and Results have given us a better way than to have nulls, and maybe golang style channels could have given us a better way to handle async?
The plan is to support async without function colouring.
I recommend this talk by Luke Wagner on async plans in WASI 0.3 (https://youtu.be/y3x4-nQeXxc) for background on how it will work.
To summarise, from an API perspective, the caller and callee won't know if the other is async or not. When calling async from non-async, an event loop is inserted at link-time and it blocks.* When calling non-async from async, the callee will get suspended if it tries to do blocking IO. That still leaves the problem of long-running compute without IO in a non-async context, but at least you're not wasting CPU time in that scenario.
* If the host runtime already has an event loop, I assume the callee will get added to that.
> You have been blocked! If you are not a malicious actor, please update your web browser to the latest version to access this website. If the problem persists, please contact support.
Unfortunate.
I think that these days the scammers have won. They can do a better job than I can at being authentic.
FWIW based on the comments I actually prefer a richer runtime that allows some of the language level features that have been hard to get support for. I think having mainstream support for effects would be sort of magical.
Actual isolated modules? Talk about living in the future.
Wasm is a lot like the jvm and flash, which makes some people hate it. But for me the problem with those platforms was the execution, not the idea.
I'm cautiously optimistic that wasm will finally give us a good high performance cross platform secure execution target for the web.
It's hard because, a lot of this stuff (async WASM, effect-driven WASM, GC in WASM, etc) is all driven by the desire to push these concerns out of wasm bytecodes, because it explodes the binary's size.
I have a pretty trivial webapp written in Yew and I stopped working on it once I saw that the wasm artifacts were weighing at 4MB (uncompressed) for relatively little functionality. THIS is what is driving the next round of "work" on WASM: to wring more functionality out of a system (WASI 0.1) designed as a drop-in replacement for emscripten output. As an aside on this point, the author is waaaay in front of their skies with basically all of their critiques around ByteCode Alliance; A lot of innuendo to basically serve a rhetorical point that isn't really true (WASI 0.1 is "good enough"). It has drawbacks! That's what the iterations on the protocol are exploring!
And this tech is cool and all, but right now a reasonably-vanilla typescript react app w/ a modern bundler (ie including stuff like router, redux, oidc, etc) is beating the breaks off of a similar app in Rust or C#/Blazor in terms of bin size. And there's no perf or API-surface argument that overcomes this. And it has chilling effect on developers when they reach for this tech.
Then maybe "this stuff" should not have been tacked onto the WASM standard in the first place? There's no free lunch. Sure, it sucks that your favorite garbage-collected interpreter/runtime bloats your module size. But the alternative option is to bloat every WASM engine by shoehorning your use case into the standard.
As much as binary size should be a problem for individual developers to deal with, I've seen how those individual developers treat Electron. There's no doubt in my mind that someone will ship 50 little JavaScript programs inside wasm modules, each with its own garbage collector. Then it becomes my problem.
Promise you won't tell the GC'd language enthusiasts this, but wasm engines supporting the wasm-gc proposal don't need to ever run a garbage collector. A simple bump allocator is 100% compliant, as long as the module's memory gets cleaned up once it terminates. Wasm engines for embedded systems will probably do exactly that, and leak memory rather than collect garbage. The web already has a garbage collector for JS, so not a big deal there either. Bloat has its cost, but at least it's only paid by cloud/edge/desktop and other use-cases that can afford to put it in their engine.
I think wasm-gc is worth it for WebAssembly. The dream is "run any language, anywhere" but it's always been easier for low level languages like C, C++ and Rust, because their binaries are smaller. Maybe with things like wasm-gc, WebAssembly can be great for C, C++, Rust, but also JavaScript and Python, Java and Go, OCaml, Perl, and whatever language gets sprung on us next.
It really depends on what you're using. If you use Rust with `wasm32-unknown-unknown`, you'll likely get small binaries (<200 kB). If you use C++ and Emscripten with all features enabled, then yeah you'll have multiple megabytes with all the libcxx and musl stuff.
It's refreshing to see that we are not alone in our thoughts on how the community is not being stewarded towards its own interests.
I applaud the author on how clear he made the argument.
For those that aim to continue working on top of WASIp1, WASIX (https://wasix.org) might be a great way to get your programs with sockets and threads fully running on Wasm.
But WebAssembly is one of the most promising opportunities in a long time to escape the C ABI problem. Why should we squander that? Do we want to be stuck writing C forever?
The C ABI approach works fine but becomes complicated when you want to pass things that aren't fixed-length, because unlike the C ABI usage when linking different libraries into the same address space, the host and the module run in different address spaces. Eg if the host wants to pass a string to a module fn, it first needs to call a different "malloc" module fn to allocate some space in the module address space and return that address, then the host writes the string to that address, then calls the original fn it wanted to call with that address, then calls a third "free" function at the end to release that allocation. Does that work? Of course it does. But the component ABI abstracts over all that for you and lets you call the function with the host's string type, and the runtime on the host side and bindings on the module side handle all the thunking so that it appears on the module side as the module's string type.
TFA doesn't seem to have any arguments other than "It's too many new words for me to learn so it's unnecessary I decided." Eg:
>And don't get me started on WIT's kebab-case identifiers (function names, interface names...). Why??? How can a specification about cross-language interoperability can come with a convention that basically no programming language use?
The point of the kebab case is that it does *not* show up in the generated bindings. It's processed by the bindings generator into the appropriate casing for the language. Eg a function name `foo-bar` becomes `foo_bar` in Rust, and a type named `foo-bar` becomes `FooBar`.
Since the point is to enable component-level interop, not fine-grained function interop, you can instead define a simple interchange format (more JSONish than IDLish) and write the library interfaces accordingly. More of a microservice approach than a linker approach.
> I strongly believe that async is the new billion dollar mistake of the 2020's: a design aberration that wasted so much developers time that it had cost billions of dollars to companies.
Yes. Nicely put
Async as a paradigm has probably created billions of dollars in that it's allowed many many many developers to reasonably scale beyond what they could've otherwise.
Just some background first: one of the billion dollar mistakes is the null pointer which is a special value to be assigned to any type. It was inherent in the first high (today considered low) level languages. Those languages (C most importantly, then Java and nowadays even Go) enabled many more programmers to create programs and they likely would not have been able to, had they been forced to write assembly. So, it's a billion dollar mistake within a trillion dollar revenue.
The async now solves a similar practical problem but in the space of prallelism (or concurrency, in some cases). A different approach might have given us the same result, but a different implementation that might be just as approachable.
Optionals and Results have given us a better way than to have nulls, and maybe golang style channels could have given us a better way to handle async?
I recommend this talk by Luke Wagner on async plans in WASI 0.3 (https://youtu.be/y3x4-nQeXxc) for background on how it will work.
To summarise, from an API perspective, the caller and callee won't know if the other is async or not. When calling async from non-async, an event loop is inserted at link-time and it blocks.* When calling non-async from async, the callee will get suspended if it tries to do blocking IO. That still leaves the problem of long-running compute without IO in a non-async context, but at least you're not wasting CPU time in that scenario.
* If the host runtime already has an event loop, I assume the callee will get added to that.
Unfortunate.
I think that these days the scammers have won. They can do a better job than I can at being authentic.
FWIW based on the comments I actually prefer a richer runtime that allows some of the language level features that have been hard to get support for. I think having mainstream support for effects would be sort of magical.
Actual isolated modules? Talk about living in the future.
Wasm is a lot like the jvm and flash, which makes some people hate it. But for me the problem with those platforms was the execution, not the idea.
I'm cautiously optimistic that wasm will finally give us a good high performance cross platform secure execution target for the web.
The irony of talking about enshittification...
I have a pretty trivial webapp written in Yew and I stopped working on it once I saw that the wasm artifacts were weighing at 4MB (uncompressed) for relatively little functionality. THIS is what is driving the next round of "work" on WASM: to wring more functionality out of a system (WASI 0.1) designed as a drop-in replacement for emscripten output. As an aside on this point, the author is waaaay in front of their skies with basically all of their critiques around ByteCode Alliance; A lot of innuendo to basically serve a rhetorical point that isn't really true (WASI 0.1 is "good enough"). It has drawbacks! That's what the iterations on the protocol are exploring!
And this tech is cool and all, but right now a reasonably-vanilla typescript react app w/ a modern bundler (ie including stuff like router, redux, oidc, etc) is beating the breaks off of a similar app in Rust or C#/Blazor in terms of bin size. And there's no perf or API-surface argument that overcomes this. And it has chilling effect on developers when they reach for this tech.
Promise you won't tell the GC'd language enthusiasts this, but wasm engines supporting the wasm-gc proposal don't need to ever run a garbage collector. A simple bump allocator is 100% compliant, as long as the module's memory gets cleaned up once it terminates. Wasm engines for embedded systems will probably do exactly that, and leak memory rather than collect garbage. The web already has a garbage collector for JS, so not a big deal there either. Bloat has its cost, but at least it's only paid by cloud/edge/desktop and other use-cases that can afford to put it in their engine.
I think wasm-gc is worth it for WebAssembly. The dream is "run any language, anywhere" but it's always been easier for low level languages like C, C++ and Rust, because their binaries are smaller. Maybe with things like wasm-gc, WebAssembly can be great for C, C++, Rust, but also JavaScript and Python, Java and Go, OCaml, Perl, and whatever language gets sprung on us next.
For those that aim to continue working on top of WASIp1, WASIX (https://wasix.org) might be a great way to get your programs with sockets and threads fully running on Wasm.
Note: I work at Wasmer (https://wasmer.io), a WebAssembly runtime.