I think WASM is an example of a thin waist [1] with its garbage collector and N+M rather than N×M. (N languages + M virtual machines + G garbage collectors). That's a mature garbage collector in V8.
I was curious if there was a WASM to JVM and it seems there is one on GitHub, I haven't used it I was just curious because the JVM is a mature (and parallel) garbage collector.
Now I'm excited for WASM Threads for true parallelism and not just IO parallelism because I didn't think WASMGC would come out so soon.
The opportunity to solve async and parallelism and garbage collection EFFECTIVELY would strengthen WASM and not be a source of confusion or difficulty for developers. I think that's why WASI is so important, a chance to define an API as stable as POSIX.
While it works well, this was mostly a fun project for me and I no longer really maintain it. I hope that the ideas and explanations of how I mapped WASM IR to JVM bytecodes helps whoever does build this in a more official capacity. I don't have any plans to support WASM GC currently.
I don't really know where to place this comment. Multi-language systems existed before, therefore Wasm should not, because it's already been done?
It's interesting that none of the languages that you list made .NET a primary target and have all either bit-rotted unofficial ports or sunsetted their mainline support. It's hard to say whether languages that now target Wasm will suffer the same fate, since that depends on level of maintenance that each project allocates to particular targets. Still, I don't really get your point.
If only the CLR had followed CLR footsteps as well, but they didn't even try after the DLR. Most non-Roslyn languages can barely interact with modern C# or the newer build configurations; even F# is playing catch-up and they're part of the official toolchain.
That is the sin of guest languages, that is why C rules on UNIX, JavaScript on Web, Java on the JVM, C# on the CLR, ....
Every guest language means additional IDE plugins with platform knowledge, since most communities want idiomatic libraries, an ecosystem on top of the ecosystem, as the actual platform is only implemented in a main "systems" language, mastering it is required anyway for all the leaky abstractions on the platform, additional build toolchains,...
In the end, each polyglot platform achieves a global maximum of main language, and possibly a winner among all guest languages, even if it takes a couple of years with projects fading away until this happens.
It sucks, however so it is the outcome of human nature attention span, and not being able to keep momentum for all languages across the whole lifetime of a given platform.
It seems like the JVM has been more successful than most platforms at supporting other guest languages. Java is still dominant, but Kotlin's pretty popular (as other comments have mentioned) and Scala has a nontrivial amount of usage. I think JRuby's also used some, but I'm not sure.
Arguably a perspective here is not that the JVM is more successful but that simply that Java as a language is less successful/more deficient language than C# and leaves more evolutionary niches available for other languages to coexist in its ecosystem, whereas C# checks more boxes generally. One reading of the history of Kotlin is that is almost directly the reason for Kotlin's existence in the first place because Java itself fell so far behind C# state of the art.
The DLR made it much easier for dynamic languages on .NET to interact and share infrastructure on top of the CLR, but it also gave C# directly just enough dynamic language powers that filled most people's use cases for that niche.
Even in the current race between C# and F#, unless you are using features such as Units of Measure or Type Providers, there is often a sense of "you don't need to use F# because that feature will be eventually in C# anyway" and "F# is just a proving ground for C# ideas". The F# community continues to try to find marketing messages that aren't that and better highlight some of the niches that F# can fill that C# won't. The most recent attempts have been trying to position F# as a "data sciences" language useful for Notebook-driven development in direct comparison/competition to Python/R/Julia. C# actually is finally useful in Notebooks now and has some alright REPL support, but F# likely will always better fit that niche. It will be interesting to see if that marketing approach helps.
> It seems like the JVM has been more successful than most platforms at supporting other guest languages
I think that’s more because of the amount of good Java binaries than because the JVM is good at supporting alternative languages.
Scala had to make a few design choices they would never have made if they didn’t want to target the JVM and be compatible with existing Java binaries.
Examples:
- having Option[T], but still having null, too.
- value types aren’t supported in the JVM (yet), so they’ve had to implement them using classes.
- scala’s reflection would have been easier to understand if they could have tweaked the JVM to implement it (scala had to work hard to map its type system to that of the JVM)
- scala has its own integer classes that are subtly different from both Java’s value types and it’s integer classes.
Kotlin mostly is Java with a different syntax. Like scala, it must be doing some magic to support its value classes.
Also, as another post in this thread said, some languages my need a garbage collector that behaves different from the JVM one. If so, you either adapt, or give up running on the JVM.
Scala 3 has an option to exempt null from ordinary types, marking nullable types as T | None, so this is not related to the JVM, this can be completely decidable at compile type.
The reflection claim is also questionable, as Scala used to be available for the CLR as well, but that version was abandoned, while the JVM is striving. In fact, erasure of generics help with guest languages, as it doesn’t bake into the variance.
> Scala 3 has an option to exempt null from ordinary types, marking nullable types as T | None, so this is not related to the JVM
I don’t know scala 3 well, but if it can make nonnullable types (that’s what “exempt null from ordinary types” means, isn’t it?), that’s not something the JVM knows about (although that may be changing with the addition of value types to the JVM)
Even if that’s incorrect, there are plenty of other examples of types that map badly to the JVM, such as Either[String,String] and AnyVal (a scala class with subclasses such as Int)
“Universal traits allow basic inheritance of methods for value classes, but they incur the overhead of allocation”
And yes, scala3 has opaque types, but in the JVM, those must revert to their underlying type (if they don’t, the performance gains are lost), and scala3 also still has value classes.
> this can be completely decidable at compile time.
It can’t the moment you load a jar and call a method in it, and I think about every scala program does that.
> The reflection claim is also questionable
The JVM has strict single inheritance. Scala supports a form of multiple inheritance through traits. There’s no trivial way to map that to the JVM.
> In fact, erasure of generics help with guest languages, as it doesn’t bake into the variance.
I don’t understand that. It surely doesn’t always help. Scala reflection doesn’t want erasure, so they’ve had to work around that.
> I don’t know scala 3 well, but if it can make nonnullable types (that’s what “exempt null from ordinary types” means, isn’t it?), that’s not something the JVM knows about (although that may be changing with the addition of value types to the JVM)
Haskell or Rust compiles to machine code, which can have invalid/null pointers as well they don’t know about. Nullness is a so-called trivial property, the compiler can prove the absence of NPEs in code output by it, so you only have to check at the boundaries (e.g. every java method will return a nullable type)
I don’t know why would these map badly to java types. Scala unifies primitives with their boxed versions and tries to smartly optimize between them, so it doesn’t have the kind of distinction as Java. Value classes require runtime support so the only thing Scala can do is to alias a primitive as another class, but optimize it away. Of course this is a leaky abstraction, but this is a fact of life on managed runtimes.
Re multiple inheritance: doesn’t map natively, but this also doesn’t need native support. At worst, scala can always just go the Groovy way and intercept any call to/from every such object of theirs and then they can literally do any form of call indirection.
Re reflection: as far as I know Scala doesn’t really require frequent use of reflection, as it can circumvent it at most times through metaprogramming/more expressive language features. Also, type erasure is the norm — most runtimes don’t store type infos.
I think Kotlin is slowly eclipsing Java on the JVM. Its not majority yet but most new projects or major refactors I've seen are either 100% Kotlin or majority Kotlin.
I would not be surprised if Kotlin takes a majority share eventually
Kotlin is Java with minor syntax changes - it brings nothing new to the table and you write it the exact same way as you would Java. It's an easy sell for shops that want the feeling they're making forward progress in the field of computing but who don't actually want to change anything.
That’s factually wrong. Null-safety alone changes completely how you write Kotlin, add to that coroutines, extension functions and dozen other quality of life improvements and you get completely different style.
It feels to me a little bit like how geographic regions wind up working out dominant spoken/written languages.
The guest languages are like relying on (human or machine) speech/writing translation services to communicate with folks who only know the local lingua franca.
That added friction hobbles the minority languages.
Perhaps AI will aid in greatly reducing these issues over time (and I say this because large language translation models and text to speech + speech to text are finally reaching professional human quality while running on local hardware today). But I can't think of any other good method to hold back the floodgates of people being forced to choose between their mother tongue (with its corpus of invaluable baked in stories and perspectives) and being understood by others.
I was just looking at the status of IronPython, and it's on Python 3.4, which has been end-of-life for nearly five years. I'm not sure that indicates practical usability for most developers. Only C#, VB, and maybe F# actually seem like a safe choice on .NET. (PowerShell too, but that's a different use case.) So, just because something has been, or can be, done "in theory" doesn't mean all that much.
Unless you were intending this as a criticism of .NET?
IronPython is indeed quite a pain to work with, and for a lot of reasons beyond the age. For instance, you don't get numpy or any dependencies that themselves depend on quirks of CPython. The alternative is PythonNET, which executes python in CPython context, but provides nearly the same interoperability* with CLR assemblies. There's many devils in the details of that asterisk, and I'm frankly not knowledgeable enough to explain them; but in my experience, things work well enough that I'm satisfied with the integration provided.
EDIT: Yes, F# is an ML-style language. I was answering the spirit of the question, which was 'statically-typed functional-first programming language running on the .NET CLR'.
> I haven't used it I was just curious because the JVM is a mature (and parallel) garbage collector.
Slightly pedantic, but the JVM is a bit more than its arbage collectors[^1]. But it is true that the JVM has been the experimentation bed of garbage collection, not because academics have been specially daring with the JVM, but because JVM programs have been used extensively, and often so in ways that strain any single GC technique.
> Now I'm excited for WASM Threads for true parallelism
Pedantic: threads are concurrent, not necessarily parallel. It's weird to see them called "true parallelism". I assume you mean in comparison to coroutines, but those are sequential (their execution order may be arbitrary, but we can rely on them not being concurrent).
If wasm adopts threads that would be another unfortunate WorseIsBetter situation, given that threads are probably the worst model of concurrency we've ever devised (other than concurrent COMEFROM)!
> If wasm adopts threads that would be another unfortunate WorseIsBetter situation, given that threads are probably the worst model of concurrency we've ever devised (other than concurrent COMEFROM)!
Like it or not, threads are here to stay. Threads with shared mutable memory are the abstraction that multi-processor systems give software. They are not a software invention (anymore, as they might have been considered in the uniprocessor era).
A key goal of Wasm is to be a portable (and safe!) abstraction over hardware. Thus exposing extremely pervasive hardware abstractions like threads is well within its mission. Doing so safely has meant careful design and specification that captures what hardware gives for memory models without forcing a new programming model or undue burden on languages, toolchains, or runtimes.
Given that there are literally billions of lines of software that use threads, hardware actually implements threads, and the entire stack is driven to optimize threads, it would indeed be a bold move to impose a different abstraction at the Wasm level. An abstraction that would incidentally be immediately used to emulate threads. That's a real layering screwup--abstraction inversion.
Also, I think your pedantry is unwarranted and a distraction. Many people accept the claim that threads are "true parallelism" and would consider cooperative (or even green) threading systems to be "not what we meant when we said threads". It's just a red herring to go off and discuss technicalities when the context is clearly Wasm exposing hardware threads.
> Now I'm excited for WASM Threads for true parallelism
Same, I'm very interested in using threads in the browser but sadly thus far threading in wasm on the browser requires the server providing an extremely restrictive response header making threads impractical for most applications.
This is giving me "wasm is the new llvm" vibes. Anyone else getting that? I understand what it's attempting to do and have seen some demonstration of that power but for the most part it is still very technically low level and using it often ends up being very cumbersome. I'm curious to know when people think it's ready for broader adoption as something you'd choose as a target over the current way of programming.
That's obviously a common misconception given the name -- LLVM was meant to be a VM early in its life, but never was, and isn't now. It's clarified in the first sentence of a home page - https://llvm.org/
It's basically a bunch of C++ libraries that implements an IR that changes over time, which help you write compilers.
Curiously, I think a decade or more ago there was a project at Google targeting the same space as WASM which made this mistake. They thought LLVM was a virtual machine! That was PNaCl or something.
I guess it's a little like LuaJIT freezing Lua at Lua 5.1 -- Lua was never a standard, but for the purposes of re-implementation, a specific version of it can be frozen. (But there are obvious problems with that approach, namely that the re-implementers don't know about all the bugs they're also freezing in time.)
---
I have raised some eyebrows at the "compromises" of WASM, but the once thing that you can't doubt is that it is in fact a virtual machine !!!
I watched a talk on the WASM GC, and the creators were up front about the compromises (e.g. you will need runtime casts at first, with the measured overhead being reasonable), which gives me more confidence in it:
I feel like I really don't get wasm. It's not clear to me what problem we're trying to solve here...
Is the goal to be able to write applications in programming languages that aren't javascript or (transpiled to javascript)? Does that include writing the parts that interact with the browser's DOM presentation layer? I think my understanding is that you can't actually do that? If that's right, is the goal instead to be able to run applications in a dedicated canvas within the DOM, like how Flash worked? If so, how big is the niche for that?
Or somebody in a different thread mentioned using wasm for cloudflare workers. Because those work via a javascript interpreter, I guess? But, like, if people want to write cloudflare workers in arbitrary runtimes, couldn't cloudflare just add support for those other runtimes? Surely they have the wherewithal to do that...
Or is there something that is uniquely great about wasm as a bytecode, beyond its connection to the javascript ecosystem?
I just really feel like I've missed reading some introductory material on why this technology is exciting, but it's embarrassing to ask at this late point...
> Is the goal to be able to write applications in programming languages that aren't javascript or (transpiled to javascript)? Does that include writing the parts that interact with the browser's DOM presentation layer? I think my understanding is that you can't actually do that?
WasmGC, which is the proposal under current discussion, is one of the biggest steps towards that. You can't safely interact with the DOM if you can't pin references to the DOM. You can't pin references to the DOM if you can't let the JS VM garbage collect you (or otherwise understand your garbage collection needs).
(One of the demos mentioned in the article shows Dart/Flutter driving what seems to be a lot of DOM via GC pointers. I haven't dug in far enough to see exactly what the balance is between JS and Dart there, but it should be a lot closer than previous demos.)
You can actually look at what wasm is currently being used for beyond its connection to the JS ecosystem. It's a really exciting space.
For example, there's a project called Oak (https://github.com/project-oak/oak) that basically allows your private information to be processed by servers without revealing them to the servers. It uses a bunch of technologies and wasm is a center piece for allowing a safe form of code execution.
For another example, check out Mozilla's post about securing Firefox with wasm (https://hacks.mozilla.org/2020/02/securing-firefox-with-weba...). Again, here we compile C/C++ code with unknown memory safety properties into wasm and then compile that to native code. The benefit of doing it over directly compiling C/C++ to native code is that any memory safety issue is strictly sandboxed to be within the wasm module. You can't have one library's memory safety bug trampling over another library's memory.
There's one huge problem WASM solves compared to other existing solutions: memory safety. Instead of running untrusted computational code in a separate process, you can run it all in a single process since WASM linear memory has bounds checking. It also combines this with extremely constrained API access giving you a way to sandbox code with almost native performance.
Besides memory checking, there are much fewer nasty things a process can do in terms of control flow since executable memory (procedures) do not exist in linear memory. It supports function pointers via a separate table system (kind of like having a separate address space for executable memory). All of this is in place so if a WASM module crashes, it doesn't take down whatever process it's running in.
Current web browsers still act as a bit of a user agent, they allow the user to inspect what is happening and block certain elements from loading or displaying. The end goal is to re-implement the web browser within current browsers but with less user control and insight, and more tracking and ads.
Look at something like wasmtime, it lets you make native programs with very secure plugins that support multiple languages by default (that even works in a browser too, in the same secure by default way).
LLVM (without a lot of coding anyway, wasmer has an LLVM backend) doesn't do the whole "easy secure sandbox by default" thing, which IMHO is easily the biggest differentiator of WASM.
Yes, they have very different security models. Wasm is capability-based, starting from zero- a module declares what imports it needs, and the code that loads the module decides how to fulfill those imports. There's no other way for the module to talk to anything outside its own internals.
This makes sense. I had been thinking of it as something fundamentally different than plugins. But just "plugins done right" is a lot more compelling to me.
This. Now that GC is bolted in there, it's like... Ok, now they're trying to go from being "the thing to run C/C++/Rust binaries in the browser" to the "we run all the things everywhere" and it's like... why?
It's just turning into, excuse my philosophy nerdism ... like Hegel's quip about “the night in which all cows are black.” An Absolute into which meaningful content just dissolves. A proverbial hammer looking for nails, and finding them everywhere, but honestly, adding no new utility.
Wasm in the browser for porting certain kinds of apps makes a lot of sense. But if you're running e.g. JS inside Wasm on a server somewhere, you got off on the wrong exit on the hype train. You don't need to do that. It's neat that you can, but ... don't.
A "universal runtime" is a kind of ultimate nerd fantasy. I've had it too. It's also mostly pointless overhead. The reality is that containerized or virtualized execution already exists and there's no "universal" garbage collection or virtual machine. They end up inevitably tailored to the language they're there to support. And that's not a bad thing.
More like, "wasm is the new Emscripten". A much more formalized standard across all browsers. It goes to the same direction as LLVM but a far cry from that.
WebAssembly goes weeell beyond browser. It ensures consistent computation among all platforms including browser, desktop (linux/windows/osx), phone (android) and iot (ARM, others)... except iOS. It's closer to JVM than anything else but really it's already so much more universal than JVM which is mind blowing.
No matter how amazing this is and how many the possibilities this opens, I can't stop thinking over the fact that browser today are extremely complex and the curve to building your own is almost too step!
You're talking about different things. You're talking about a browser that can work with some web pages. Not too hard!
He's talking about a browser that works with all the web pages. That's virtually impossible. Even Microsoft gave up. Mozilla are just about hanging in the fight but I would not at all be surprised if they fail at some point.
Browsers that only work with some pages are interesting to nerds, but normal people will use Chrome because it's still a great browser and it always works.
Building your own Linux is impossible too, but no one bitches & moans about that. No one slams Android for saying they couldn't go make their own easily.
Why would the world's most successful & available hypermedia platform need to be something one can easily implement? Of course the most successful online system in the world is complex & featureful. That's why we use it & it has won.
> Building your own Linux is impossible too, but no one bitches & moans about that. No one slams Android for saying they couldn't go make their own easily.
you’re literally just not in the right circles.
> Of course the most successful online system in the world is complex & featureful. That's why we use it & it has won.
combine these two and you get the meme about IRC users (a protocol you could implement in a day) who refuse to leave that ecosystem. so as a starting point, if you’re looking for the people who are passionate about that kind of simplicity, you’ll have to meet them there. i’d start with the alternative kernels/OS (serenity, 9front, hurd) and then maybe the language side of things (Hare certainly has a prominent dev known for ranting about complexities). either way if you’re willing to dig, you absolutely will find the viewpoints around simplicity which you posit don’t exist.
Gopher and Gemini might be up your alley too. not as low in the stack as the others, but especially the latter's a pretty direct response against "Why would the world's most successful & available hypermedia platform need to be something one can easily implement".
I admit & admire that the story could change any day, & these tiny little niche communities could become relevant & important.
But there seems to be this attitude specifically against the web that the web needs to remain small enough for anyone to DIY quickly. That's just not how you get the most popular most useful most interesting hypermedia canvas on the planet.
The desire to reduce the web down is a sick one, malicious projection of a limited conservative worldview, on something that has become so great because we have cared about it enough to grow it.
> Linux From Scratch (LFS) is a project that provides you with step-by-step instructions for building your own custom Linux system, entirely from source code.
Yeah it's a perfect example of a one man OS and a one man browser! Shows it's technically possible.
I still think what-one-person-can-solo-recreate is an awful terrible gate to limit what software is acceptable by though!
I look at how quickly & how competently people can make interesting powerful software systems on the web, that they can share & that can connect people, and I am in awe. Prioritizing recreating the platform is so low on my list of priorities: it's this time to power for devs, new & experienced, that seems like the better goal. And here the web just trounces everything else.
With just vanilla and some basic html one can take a page & very quickly begin to make it dance, in all kinds of wild ways.
I can take someone with a couple days of programming & pair with them & in a couple hours we can write a user script to rewrite some kind of weird or suboptimal computing experience they're already having, show them how easy it is to modify & improve their life & the online things they go through. That power is just amazing, and that there is so much freedom under the hood to be creative & do great things: that's thrilling & exciting for us all.
Time to start: fast, but with deep reserves of capabilities one can always tap into: that's greatness. The web's power curve is so much more exciting than anything else I've ever gotten to share with newcomers to the field. Connecting newcomers to computing has never been better, thanks to the great & powerful web.
That’s true, but this work is a foundation for simpler alternative runtimes that don’t need an entire browser engine to run. (consider Node or Deno, which rely on V8.)
> extremely complex and the curve to building your own is almost too ste[e]p
a lot of modern things have reach this point. You cannot possibly build your own car. You cannot possibly build your own house from scratch (too much of it require specialist knowledge, like building code etc).
I'm impressed that this finally got built and released; I've been hearing about WASM planning to include GC for several years now, I wasn't sure if it'd ever actually happen.
I'm curious how much this will help some of the WASM-targeting languages that have issues with large binaries from building their runtime. IIRC, Blazor requires something like 1 MB just for a hello world; I wonder if WasmGC will help with that.
It may take some time for WasmGC to be usable by .NET. Based on the discussions the first version of WasmGC does not have a good way to handle a few .NET specific scenarios, and said scenarios are "post-post-mvp". [0]
My concern, of course, is that there is not much incentive for those features to be added if .NET is the only platform that needs them... at that point having a form of 'include' (to where a specific GC version can just be cached and loaded by another WASM assembly) would be more useful, despite the pain it would create.
Interesting, thanks for the background. Reading through that GitHub issue helps me understand what other comments on here are talking about with the differences between various GCs making it difficult to have a one-size-fits-all WasmGC.
> Chrome's Wasm team has compiled versions of the Fannkuch benchmark (which allocates data structures as it works) from C, Rust, and Java. The C and Rust binaries could be anywhere from 6.1 K to 9.6 K depending on the various compiler flags, while the Java version is much smaller at only 2.3 K! C and Rust do not include a garbage collector, but they do still bundle malloc/free to manage memory, and the reason Java is smaller here is because it doesn't need to bundle any memory management code at all.
I saw that, and it's impressive about Java; I thought it would require a similarly large runtime to .NET. From the blog post, it sounds like they're not shipping a JVM, I guess they're just shipping as little of the JRE as they can get away with? (I'm guessing due to my unfamiliarity with the Java world)
.NET's runtime gives it capabilities like finalizers, JITing, debugging, etc. - it's very comprehensive. J2Wasm (Java on WasmGC) is making do without those for now, and just shipping minimal support for a large/useful subset of the language.
The new wasm support in Kotlin is pretty exciting. There's an experimental version of compose multiplatform that supports targeting browsers that will be using WASM. Compose multiplatform is basically Google's Jetpack Compose for Android with added support for other platforms.
As of a few days ago, IOS support in alpha and will hit beta next year. Android and desktop support are now stable. Once that stuff stabilizes, you can write UI applications that basically work on any platform.
The wasm compiler will roll out along with kotlin 2.0 which is the next major release for Kotlin and features a new compiler (k2). This looks like it should happen early next year. k2 is currently available in beta release and you can enable it in kotlin 1.9.x.
The nice thing with the Kotlin multiplatform ecosystem is that there are already a lot of libraries that work across the various platforms. So, the wasm compiler will rapidly become part of that and inherit a lot of nice libraries. Mostly all that takes is for builds to be reconfigured to target that ecosystem and implementing some platform specific behavior where that is missing.
Another interesting thing in this space is using and linking libraries written in different libraries. For example a lot of the platform specifics are probably going to be dependent on e.g. C or Rust libraries that are already available. In many cases these might be the same libraries that Kotlin native uses.
How does Compose Multiplatform on web work? Does it paint on Canvas like flutter?
Do you use the same widgets on Desktop, web, iOS and Android? I understand that on Android it's native. Does it emulate other styles on other platform? I guess, my question is mostly: How is the _feel_ of a Compose Multiplatform App on non-Android platforms.
Dart on web e.g. feels extremely janky to me, so does electron on Desktop. Dart on Desktop is ok, but all the applications tend to be very App-like with huge buttons and whitespace that just make it feel more like it was an Android App running in an emulator rather than an actual native app.
Would you recommend Kotlin as a language for new projects?
Compose web indeed uses a canvas and just like the Android and IOS versions it renders using low level graphics libraries such as cairo. I think the intention is indeed to have the same core components across all platforms and indeed emulate the look of the native platform. This is similar to what things like flutter do, which also depends on cairo if I'm not mistaken.
There is also compose web html which seems to be not getting a lot of attention lately. That depends on kotlin-js and works with the dom tree. But for obvious reasons it's a lot less easy to use the same components there.
I think it's too early to tell how it will perform, look, and feel on every platform. This is also a bit of a function of how much effort and design you put in. Using wasm in the browser should help with performance though. Dart might copy that trick as well. I think they currently cross compile to javascript.
Kotlin as a language is more than fine. Anything server related or Android related, it's a great language and I vastly prefer it to Java at this point (27 years of experience). I personally use it for web development (kotlin-js in the browser) and server development (spring-boot, ktor). IOS native development for libraries is also a popular option for sharing code across IOS and Android. That just got promoted to a stable release. There are some gotchas with interoperability with e.g. Swift and Flutter from what I understand. But it's basically very stable and workable at this point and fairly widely used.
Compose multiplatform is not quite ready yet. The Android and Desktop side work fine and are stable. But the rest is a work in progress. Compose web is going to be a while because even the compiler isn't ready yet. Compose IOS looks like it should get to a usable stage in the next six months or so. Stable is probably at least a year out for that.
How does this work in regards to accessibility? I think historically, canvas has not had a great track record as it's essentially a black box. While it might be okay for certain use cases (intranet, one-off tools, etc), I don't see how this would ever take off on platforms with a public user facing application.
Compose on android is native as in it takes full responsibility for its canvas, not native as in react native which tries to operate pre-existing controls as much as possible.
That's what this WasmGC is for :)
Dart on web runs as dart2js whereas other platforms like Android/iOS/macOS/Linux/Windows runs as AOT arm64 or x86_64 code. Dart on wasm (as shown in demo in the article) will run better than transpiled JavaScript.
Same. Also at least based on what I’ve seen on Android there’s a decent chance you’ll be stuck using some Java libs due to lack of native Kotlin counterparts, which makes it harder to fully leverage Kotlin’s features wherever you need to work with those libraries.
The syntactically similar language I’d rather have run everywhere is Swift. Swift Package Manager has problems too of course but in my experience has been less maddening, and the community has been more aggressive about writing native Swift replacements for Obj-C libraries.
Just that Java libraries aren’t going to be taking advantage of Kotlin-specific language features, which in turn makes it harder to use those features in the parts of your application code that interact with those libraries unless you write wrappers that make these libraries fit in better with your Kotlin codebase.
There’s a similar issues with using Objective-C and C libraries in Swift, which makes Swift libraries more appealing where available despite Obj-C/C interop being good.
Gradle is cancer.
I would love to recommend an alternative that enjoys the admirable quality of "not being cancer", but I am instead left not developing on Android out of lack of any coherent alternatives (and especially examples or tuts showing how to make use of them if they do exist hidden out in the wild there somewhere)
EDIT: just to add some background, I am staunchly against any development platform that requires a developer to build a ridiculously complicated nest of file structures, libraries, bindings, etc out of dozens if not hundreds of disparate and usually incompatible projects yoinked from the internet that further makes broad and undocumented assumptions about the dev machine you're trying to install this all on.
Also bear in mind that package managers by themselves often make this process worse instead of better because they then proceed to proliferate into an entirely different level of the trashfire hierarchy. Get node/npm even though you're not coding in Javascript, then still have to get chocolaty or flatpack or fall back to trying to insulate everything into a docker container or use pip to install python packages but then drag out the venv when different projects want different versions of the packages and then further drag out conda when they want different versions of python as well.
Too much of the environment assumes that the developer is already steeped in all of the thousands of compromises existing developers have slowly gotten accustomed to over years of yielding and putting up with it. Another day, another fault line along which to fragment every tool you need just to create "hello world".
This is a good description of my problems with Python when it was first introduced to me. Although, "hello world" didn't require installation of anything (Python has been pre-installed in Ubuntu for a while now), the mess and confusion and incompatibilities and irreproducibilities were quickly run up against and each "solution" was more opaque and flippantly suggested that I couldn't figured that programming wasn't for me. I'm glad I persisted past it. The juxtaposition between hearing that "Python is so easy and wonderful" and my experience made me look for answers. And the answer, it seems, is that people don't care. The vast majority just keep installing everything until it works and don't mind that they initially don't know what's going on (and oftentimes never find out). The few that do mind are on opposite edges of a canyon of experience.
Gradle might be fine compared to other Java build tools, but when stepping outside the Java world it feels like a leftover from the 90's... and that's the same for pretty much anything else related to Android development, which is curious because Android didn't even exist yet in the 90's ;)
Which build tool is better? The language-specific ones are pretty much useless for a more involved build that is cross-platform, let alone uses multiple languages.
Gradle is generic, which does increase its surface area, but there is not many alternatives that are also fast and correct: there is bazel, and a new thing I very like: mill. But not much else is there.
Gradle is almost a language itself. I have never seen a project where teams didnt run into gradle hell and spend hours on every lib update to fix gradle issues. Its just too damn complex and most of the time nobody is really an expert in it.
Right. The thing is that how it works changes all the time. A construct you used in gradle file suddenly gets deprecated or outright not working with new gradle versions. I heard those things happen during minor version upgrades as well. No thanks! Luckily I don't work on android apps.
Can somebody explain to me why this blog post (and the chrome announcement blog post) does not mention go? This gives me the impression that go can't benefit from these changes, even though it's garbage collected.
That’s not an example of interior pointers as used in Go.
In Go you can have a fixed-size array of structs, where each struct contains many fields, including a fixed-size array field. The whole thing can be allocated at once and is automatically zeroed out. And then you can take a pointer to any struct’s field or any internal array element.
“Interior” means the interior of a single object. A pointer to anywhere within an object prevents it from being collected.
It seems that you can work around this by allocating every field separately. This is obviously going to perform worse but I don't think it would actually violate the language behaviour (other than maybe assumptions using unsafe memory access). Combined with some whole-program analysis to see which fields ever have their address taken this could even be a relatively small performance hit. (Although I think this whole-program analysis would fail in the face of reflection.)
Depending on how much code you want to run it may be interesting to be able to decide between shipping a GC implementation and slowing down your code to avoid it. If it is an extra 50% code size on top of a fairly small amount of first-party code you may pick WasmGC.
Can’t it be compiled as a struct of a normal pointer and an “index” to a field/offset whatever? Not the most efficient, but with value types it might not be too bad.
That's what 4ad meant by "something ugly like fat pointers".
The overhead compared with interior pointers is relatively high due to the extra space for the fat pointer index, which space is taken everywhere a pointer could be an interior pointer, even if it's rare.
When a language runtime can optimise in whatever way it needs to, if interior pointers aren't an option there are various schemes to avoid the fat pointer space overhead and replace it with time overhead, but they often involve using unused bits in the pointer word, or recognise certain address ranges. Unfortunately, I assume those techniques are incompatible with a GC like WasmGC's, which requires all pointers to use the standard pointer representation, so prevents these kinds of space and time optimisations.
Probably, but it means all pointers need that representation because any pointer could be an interior pointer. If you’re given an arbitrary pointer, it could point somewhere within any object. (Unless it can be proven otherwise using escape analysis.)
Whether a pointer is an interior pointer or not isn’t part of its type in Go.
> Code in [C, C++ and Rust] that does any sort of interesting allocation will end up bundling malloc/free to manage linear memory, which requires several kilobytes of code. For example, dlmalloc requires 6K, and even a malloc that trades off speed for size, like emmalloc, takes over 1K. WasmGC, on the other hand, has the VM automatically manage memory for us so we need no memory management code at all—neither a GC nor malloc/free—in the Wasm. In the previously-mentioned article on WasmGC, the size of the fannkuch benchmark was measured and WasmGC was much smaller than C or Rust—2.3 K vs 6.1-9.6 K—for this exact reason.
Would it make sense for the runtime to expose an inbuilt alloc library that C/C++/Rust can use? Programs could opt in to that library instead of bundling their own allocation library.
From a pragmatic viewpoint of saving some space, it might make sense. But from a design viewpoint it's not really a "minimal necessary feature" to do something you can't do already, so specifying it as part of the standard just bloats things up for no reason. What I mean by that is, the Wasm GC primitives were designed the way they are to be the smallest possible additions for the best uplift. When other primitives are added like SIMD, it's because they're the "roughly minimum primitives" to achieve those features. You want the smallest possible building blocks, there.
But a memory allocator API for malloc/free or whatever is not a minimal feature. A memory allocator is actually a bunch of policy and design decisions, and the algorithms used by the allocator will flow from those. The fundamental building block there is just "a linear piece of memory owned by the allocator", and that already exists. It's the linear memory WASM has had since day 1!
It might make sense for individual runtimes to do it though, i.e. give some built-in wasm import a piece of the linear memory and let a runtime-implemented allocator handle it. But it would almost certainly never become standardized I bet, and almost all of the tooling for non-GC'd Wasm languages is basically designed around porting working, existing codebases, including their allocators. So it's just more code to maintain for one-off implementations. The juice might not be worth the squeeze.
> Would it make sense for the runtime to expose an inbuilt alloc library that C/C++/Rust can use?
A difficulty with that approach is that the alloc library's behavior would need to be standardized and deterministic. If it isn't deterministic then differences in program behavior can end up mattering. For example, if one browser ships a "smarter" alloc that reuses memory better then it could succeed to run a program that another browser ends up with fragmentation in linear memory and an OOM. Historically browsers have worked really hard to standardize behavior to avoid such differences.
And standardizing the behavior of a malloc/free implementation is tricky for several reasons. First, there is a lot of complexity there - how you handle free lists, what chunk sizes do you use, etc. That's a lot to put in a spec. Second, once you standardize a particular behavior it can't be improved, and new ideas for better malloc/free are still being found.
For those reasons I'm skeptical of this making its way into the wasm spec.
In comparison, GC avoids these problems because there is nothing to spec: the pointer values are not observable.
The internal details of an allocator are not part of its API: you can swap out malloc in any conforming program with one that manages its memory completely differently and it will, for the most part, still work. This is regardless of whether it uses freelists or arenas or space partitioning or whatever. Sure, some allocators are worse than others for certain workloads, but no browser will go out of their way to match the exact resource usage profile of another browser. It is very easy to hit performance differences in algorithmic complexity across browsers, much less one OOMing earlier than the other because it allocates objects that are larger.
I'm a bit skeptical about this. This is significantly increasing WebAssembly's complexity.
Garbage collectors are a leaky abstraction: Some support interior pointers, others don't. Some support parallel tasks sharing memory, others don't. Some require compaction, making C FFI more difficult, others don't. Some require a deep integration with whatever mechanism is used for green processes/threads and growable stacks, others don't. Etc.
When looking at languages like Erlang, JavaScript, Python, or Go, the choices made at the language level are partly reflected in their garbage collectors.
That idea of a universal/generic VM supporting many languages has been tried many times, with limited success, for example with the JVM, CLR, or Parrot. What makes this different?
>That idea of a universal/generic VM supporting many languages has been tried many times, with limited success, for example with the JVM, CLR, or Parrot. What makes this different?
What's different this time is that WASM flips the timeline backwards from how Sun JVM and Microsoft CLR tried to do it.
- JVM/CLR : create virtual machine & IL instruction codes specification & runtime first, then later try to distribute widely as possible and hope for universal client adoption. In other words, the "intermediate language vm" will be so compelling that it causes industry to spread it throughout the landscape. This hope was only partially true. JVM/CLR did spread on desktop & servers but fell short on web browsers with Java Applets and Microsoft Silverlight. JVM/CLR also never got widely adopted on mobile platforms.
- WASM has opposite timeline : take something (aka Javascript) that's already distributed and adopted industry-wide and work backwards from that to create a "virtual machine & IL instruction codes specification & runtime".
In that view, Javascript (the so-called "toy language") was the 20-year long "Trojan Horse" of getting wide distribution on all clients first. Now the industry says: "Hey, did anyone notice that we finally have a universal runtime (Javascript) on server+desktop+browser+mobile?!? Let's make it fast by creating an IL runtime!!!"
There were a few technical issues such as Sun JVM not having raw pointers which makes it not a good performing target for pointer-based languages like C/C++. And MS CLR wasn't available on macOS (except for Silverlight minimal-CLR). But those technical limitations don't have as much explanatory power as the Javascript-as-harmless-trojan-horse-distribution timeline.
Java applets came out at the same time as the original release of Java, and were definitely part of Sun's strategy for Java, but providing a platform for other languages wasn't in the plan. Others came because they specified the virtual machine so clearly that it was actually possible to target (unlike Python's), but if you look at the Java bytecode it's pretty clear that it exists to serve one language and all others must adapt. You calling the JVM not having raw pointers a "technical issue" makes it sound like it was a mistake on Java's part, but that's just a side effect of only intending to support a language that doesn't have raw pointers.
Meanwhile, WASM isn't just JavaScript-made-fast—JavaScript is already very fast through JIT compilation, and it's not obvious that compiling it ahead of time to WASM would make it faster. I'm not even aware of any efforts to do so. WASM is actually what you seem to think Java was—a new runtime built for the web and designed to allow many languages to target it flawlessly. To the extent WASM has an advantage over previous shared runtimes, it's that it was designed to be shared from the start and was spearheaded by the largest modern platforms—the browsers themselves are the true Trojan horse here.
> take something (aka Javascript) that's already distributed and adopted industry-wide and work backwards from that to create a "virtual machine & IL instruction codes specification & runtime".
I don't understand this. WASM is not JavaScript: it can't do things that JavaScript can do (such as directly access the DOM) and JS can't do things that WASM can do (such as pointer arithmetic). Their abstractions are totally different, which is why they complement each other. JS does not run in the WASM virtual machine. Other than the fact that they both run in a web browser, they have almost nothing in common.
So I think your analogy fails. WASM is not a JS IL, it's a separate IL that happens to be distributed by the same mechanism. As such, it can still fail and is still vulnerable to the hubris of the JVM, wherein a single memory model was selected for all platforms and all (higher-level) languages.
>I don't understand this. WASM is not JavaScript: [...] So I think your analogy fails. WASM is not a JS IL, it's a separate IL
Yes, that's what it looks like now. To explain the Javascript-to-WASM evolution, we use the concept of the "Overton Window" shifting the industry by almost imperceptible degrees :
https://en.wikipedia.org/wiki/Overton_window
- phase 1: 1995 Javascript the "toy language" but no IL. The industry doesn't need to be "afraid" of a toy scripting language because it's just there to make the monkey dance.[1]
- phase 2: 2000s Javascript is being extended XMLHttpRequest() to create "serious" apps like drag & drop email and Google Maps
- phase 3: 2013 extensive use of Javascript everywhere motivates a performance hack by creating subset in "asm.js" : https://en.wikipedia.org/wiki/Asm.js
- phase 4: 2017 instead of "asm.js" (and also using some Google NaCl ideas), the industry finally collaborates to create WASM to further optimize away the inefficient "asm.js"
Since WASM today looks a lot like what JVM/CLR hoped to accomplish, why couldn't we have skipped the whole circuitous route of Javascript-then-WASM instead of just having "WASM in 1995" in the first place?!? Because of the Overton Window. If WASM was there from the beginning, it would have been too "threatening" in 1990s and corporate firewall admins would want to block all http/html that had it. ("Let's prevent WASM viruses") Javascript on the other hand is just for animating monkeys so there's no need to block it. That's how you eventually get universal distribution across clients including smartphones. With Javascript code already entrenched everywhere, it's easier to slide the Overton Window over to WASM.
WASM didn't get created in a vacuum. It's existence is directly tied back to Javascript's usage and growth.
> Since WASM today looks a lot like what JVM/CLR hoped to accomplish
WASM is actually much lower-level than JVM or CLR, there's basically no comparison. The main feature in both CLR and JVM (accounting for the bulk of their complexity) is their object model which has no equivalent in WASM-GC, the closest thing (while quite different nonetheless) is probably the WASM components proposal which is still vaporware.
(Could WASM-GC have shipped in the mid-1990s? Quite unlikely, the FLOSS community was in its infancy back then and JVM applets were seen as state of the art. Even the Cyclone language was only created in the mid-2000s, and having full type- and memory-safety with C-like performance and no need for pervasive GC was unthinkable prior to that. The plan9 folks had Limbo and Dis which were somewhat simpler, but there was zero broader interest in using something like that over the JVM.)
Not to mention, JavaScript engines were essentially extended into supporting WebAssembly much like they were extended to optimize Asm.js. Browsers generally don't have entirely separate WebAssembly engines, parts of their existing JS engine are shared for WebAssembly. I think that makes the case pretty well!
That doesn't really make the case—JIT compiler backends are all pretty similar, so of course the browsers are going to reuse parts of their JavaScript JIT for WASM, but that doesn't make WASM "JavaScript, but fast". The only resemblance WASM has to JavaScript is it lives in the browser.
To be fair, as far as I can tell you are the first person to use the phrase "JavaScript, but fast" in this thread. Wasm's design isn't based on JS, obviously. It is, however, strongly inspired by ideas like asm.js and the existence of C -> JS compilers (among other things.) You can find more evidence of this than just HN commenters. Here's Google:
Heck, the same toolchain (Emscripten) that targeted asm.js became one of the defacto Wasm toolchains. They're not the same thing, but the path from which things evolved is not really open for much debate.
> Now the industry says: "Hey, did anyone notice that we finally have a universal runtime (Javascript) on server+desktop+browser+mobile?!? Let's make it fast by creating an IL runtime!!!"
If they'd said that we have a universal platform in the browser, make it fast, that would be different. It's the idea that the JavaScript runtime is going to be made faster by WASM that I'm objecting to.
>>"Hey, did anyone notice that we finally have a universal runtime (Javascript) on server+desktop+browser+mobile?!? Let's make _it_ fast by creating an IL runtime!!!"
Ok, I see the misunderstanding. The "_it_" I'm referring to is the "desire to run serious apps in that runtime". I wasn't claiming that WASM makes Javascript syntax faster.
jchw intepreted my meaning as I intended: From my PoV, they designed an IL runtime based on how some people were using JavaScript, aka inspired by Asm.js, not an IL runtime for or based on JavaScript.
My point is that history has now shown us that it's easier for Google to extend V8 engine to also run WASM, and for Mozilla to extend SpiderMonkey to also run WASM... rather than for Sun JVM applets to spread into all browsers as a universal runtime -- or for a JVM-bytecode-compatible engine to be embedded inside Chrome/Firefox.
Even though Javascript itself was not purposefully designed to be a universal compilation target for C/C++, it nevertheless opened a door to a universal bytecode runtime that the JVM/CLR did not.
Interesting. I think we are interpreting that statement quite differently.
From my PoV, they designed an IL runtime based on how some people were using JavaScript, aka inspired by Asm.js, not an IL runtime for or based on JavaScript.
I did not read that statement as saying Wasm was or was intending to be faster JS, at least not generally. Of course you are correct in that Wasm has absolutely nothing to do with JS on a lower level.
> 2013 extensive use of Javascript everywhere motivates a performance hack by creating subset in "asm.js" :
ASM.js was an attempt to make JavaScript a useable compilation target, not a performance hack for JavaScript programs. That effort did lead to people deciding that a new bytecode format was a better option, but the desire to compile to JavaScript was never about JavaScript itself, it was about trying to bring other languages to the frontend web. At the time the only way to do that was to compile to JavaScript.
I don't think you're wrong about the Overton window, but it's a lot more complicated than "JavaScript shifted the overton window". The actual history is one of people wanting to do more and more with web browsers in general until JavaScript became non-viable. WASM is far more a younger sibling of JavaScript than it is a descendant of it—both were born from the growth of the web.
> That idea of a universal/generic VM supporting many languages has been tried many times, with limited success, for example with the JVM, CLR, or Parrot. What makes this different?
I would say that the JVM and CLR were not designed to support many languages. The JVM has one master, which is the Java language, and other languages that want to run on the JVM do so with a burden that increases as the distance from Java's type system increases.
Wasm is lower-level than most of the above bytecode formats. Even with Wasm GC, which adds statically-typed structs and arrays, and the function-references proposal which brings typed functions. With Wasm GC, there is also explicit support for tagged pointers (i31ref).
All of the above makes Wasm GC more general (by virtue of being lower-level) than the efforts you listed.
Microsoft planned to support all kinds of languages and the list of CLR languages is respectably long, but most of them can't really keep up with the co-evolution of .NET and C#.
The generics in .NET 2.0 were the first big compatibility break, and I think async/await was the second and even bigger one that killed off the bulk of alt-langs.
async/await was never a compatibility break at the CLR level. Most of async/await is "just" the TPL (Task Parallel Library), a subset of the .NET BCL (Base Class Library) and entirely handled as libraries, which any language can use/interact with even without async/await language support.
The async/await language support in C# is basically a Monad transformer, building a generator-like asynchronous state machine. (The output of an async/await function in C# is rather similar in concept/implementation to the output of a generator function using yield syntax.) This is also why F# has never had a tough time with async/await because it has even more generic Monad transformer support ("computation expressions").
Any "alt-lang" could make use of the TPL whether or not they could transform their own similar syntax, and there's nothing stopping them from building a similar syntax. As far as I saw that wasn't a killer of languages.
It wasn't a break at the CLR level, but it was a break at the BCL level. To consume the new async methods your language had to come up with a mechanism that was broadly similar to how C# worked or miss out on the vast majority of libraries and the BCL itself.
If a language didn't do anything at all like C# it dealt with a lot of task.ContinueWith(value => {}) callbacks (Action<T>), no different to JS promise.then(value => {}) chaining (truly, no different at all because Task/Promise are basically twins). At worst case you had callback "waterfalls" to deal with, but that was hardly "breaking" and shouldn't have "killed" languages. async/await is much nicer than callbacks/callback waterfalls, but that doesn't mean you can't write callbacks/callback waterfalls (a lot of JS projects certainly did; many still do even despite most browsers supporting async/await today).
The language would also need to have some support for anonymous delegates and closures, or its callback waterfalls would have to be broken up into multiple methods that would have to manage the call state themselves.
CLS really expected most languages to look a lot of like early C#, that is, Java with some Delphi/VB features grafted on.
> But CLR stands for Common Language Runtime and had three official languages shortly after release (VB.NET, C# and J#, offhand).
CLR is not really common -- the "tells" are VB.NET and PowerShell.
VB6 was a very popular and useful language, and VB.NET broke everything in that ecosystem, turning it into a variant of C# with different syntax.
Does that sound like a multi-language VM?
If so, why not compile VB6 to it?
PowerShell is likewise best described as C# with a horrible shell-like syntax.
It's not really a shell in the original sense, because it doesn't talk to the kernel directly -- it runs on a VM, which mediates all access to the kernel.
fork() is a syscall that shells rely on critically, which is problematic for almost all VMs.
Some notes on "interior" (VM-based) vs "exterior" (OS kernel-based) shells here:
It was intended to be common whether or not it succeeded.
Those examples point to a differing design philosophy at Microsoft rather than some inability to write a language on the CLR. The design philosophy was clearly to do Java-ish typing across the board, supplanting VB6's loose typing and bash's text based mindset.
I don't really like the approach. I think shell scripting succeeds because it allows ad-hoc programming and I don't think Java-ish weak but static typing is some perfect ideal to be used everywhere, but you could certainly implement a more loosely typed language on the CLR.
Maybe sticking to the one true universal bytecode format is already a fallacy — why not just stick to AST interpreters, when they can be made efficient? This is the premise of Graal’s Truffle, and I think they succeed at it quite well.
The abstract syntax tree is then traversed and
encoded into a stream of symbols from the evolving
vocabulary. The encoder processes whole subtrees of
the abstract syntax tree at a time; these roughly
correspond to statements on the level of the source
language
If I'm not wrong, this technique originated in Oberon and was polished by the paper authors (disciples of Wirth)
It still has C#'s object type system baked into it. It also has reified generics (IMHO a good thing from a performance perspective), which bakes in a particular way of doing parametric polymorphism. If your language's generics work differently than C#'s, you are probably going to have a bad time, and you might have to bring all your own libraries.
The CLR's support for functional programming is better than the JVM partly because of the reified generics and partly because Microsoft developed F# and thus had skin in that game and worked hard to make it better.
The JVM's support for functional programming has always been weak; invokedynamic and the additional MethodHandle framework reduces boilerplate but is not really the right feature for FP, but a very clunky and heavyweight OO view of FP.
> It also has reified generics (IMHO a good thing from a performance perspective), which bakes in a particular way of doing parametric polymorphism.
Reified generics need additional support, and if you're using type erasure you didn't need any support. What's missing then when it comes to supporting mainstream languages - HKT?
I think it's also worth pointing out that the main Go implementation has a non-moving GC (at this time), and all kinds of fecal matter may impact a rotary ventilation device if and when that changes. Like, are we supposed to pin byte arrays we pass to syscalls? Nobody does that, right now, for short-living calls.
People have certainly written unsafe code which uses uintptr in naughty ways which only work without moving gc, but every piece of documentation for such features also tells you not to do it.
GraalVM has multiple language implementations that can now run on both GraalVM and OpenJDK.
They are typically as fast and sometimes faster than their standard counterparts (e.g. can't find the announcement now but mastodon runs much faster on GraalRuby than on JRuby, and JRuby is pretty fast).
I have personal experience with GraalJS being very fast.
So I _think_ that you could now say that the JVM can now properly support multiple languages. There are still limitations, mostly related to language libraries that have native bindings so it's not all flowers and chocolates. But still worth considering. But as a general purpose multi-language platform I think you could say it's viable now.
But I share the scepticism - is this really the point of WASM? Whether it is or not, it does seem to be the general direction
The WASM-GC support is intentionally kept as simple as possible, see https://github.com/WebAssembly/gc/blob/main/proposals/gc/MVP... . Building a "universal" or "generic" VM supporting many languages on an equally seamless basis is explicitly not a goal of WASM-GC; it is expected that each implementation might require its own hacks and special semantics on top of the basic support WASM provides, but this is okay because FFI/cross-language interop is seen as an entirely separate issue.
Fair enough. I'd say the JVM and CLR are quite important and "sticky" as well on the server-side ;) But on the client-side, there is no contest, JavaScript (and thus WebAssembly) are the only option.
That idea of a universal/generic VM supporting many languages has been tried many times, with limited success
What wasm is doing is something different than previous efforts. The gc facilities aren't provided for the sake of interop with other languages, or for the sake of sharing development resources across language runtime implementations. Wasm is providing gc facilities so that managed-memory runtime languages can target wasm environments without suffering on account of limitations imposed by the restrictive memory model, and secondarily to reduce bundle sizes.
Wasm can potentially support more tunable gc parameters to better suit the guest language's idiosyncrasies than can other general purpose language runtimes. And unlike the runtimes we're comparing it against, language implementers don't have to option of making something bespoke.
> I'm a bit skeptical about this. [...] Some support [X], others don't. [...]
I am super conflicted, but I err on the side of agreeing with you here. Gut feeling says it's too complex and fragmented to be "standardized".
Generally speaking, "abstracting out" something should be done when that "thing" is actually pretty clearly defined and always end up behaving the same way. My understanding is GC does not qualify - despite its popularity we still don't know where the cut points are, in all honesty. There's a reason why languages differ so much from one another, and afaik this is still an area of quite active research.
Adding to that, intricate understanding of GC internals of multiple languages is probably restricted to a very low amount of engineers, worldwide. This makes it very difficult for curious bystanders to understand, follow and critique the progress. Remember that this is a massive career project for the proponents, so they're also incentivized to push ahead and perhaps gloss over legitimate issues.
And what if the design ends up being flawed, yet wildly popular? Then languages may be forced to conform to the WASM GC model in order to stay "modern" and attractive, while constraining their normal design space.
That said, if everything goes dandy it can be a huge win, so I can relate to the excitement as well.
The correct question is “what makes WASM different”, not the other way around. And, mostly nothing, besides some “elegance” from being a new thing not yet full of legacy constructs. Oh, and that the browsers (or whatever is left from the browser scene with nigh everything being blink unfortunately) agreed on this one and not the others.
I personally think that GraalVM’s truffle infrastructure is much more impressive, and supporting the much wider managed language landscape may have been a better idea for wasm as well.
As I understand it, it's just difficult to efficiently implement GC because of the stupid decision to abstract the call stack within WASM, making it painful to get at stack pointers during collection.
One would think that a generic VM assembly and the ability to make pages of memory as no longer in use so the VM and then OS can recollect them would be sufficient.
Rather than admitting to and fixing their mistake, they're doubling down and doing something of much greater complexity to work around it.
I see what you're getting at. I think something like code pointed integrity would be more interesting, and would even protect pointers in vtables and other heap allocated structures:
I don't understand why more languages don't just handle a separate data stack from the call stack. Then the call stack can remain harder to screw up and you can continue using stack frames for arguments or locals as desired.
When feeling productive (not much in the past year unfortunately) I've been trying to build a bytecode for i86/x64 that operates this way.
> the JVM, CLR, or Parrot. What makes this different?
I think primarily because the decision to run a certain language on WebAssembly comes first and now the question is how to make it fast. For the JVM or CLR there was usually no need: Just pick Java C# or run the other language natively.
The blog post goes into this topic. It's basically a question of trade-off between performance and exact semantics, in some cases you will need to sacrifice minor semantic differences for meaningful performance gains. I think this is rather common when porting languages to "foreign" VMs and doesn't seem to be a showstopper. The application developers targeting WASM and other platforms of course have to test on all platforms and will avoid those cases with semantic differences.
The main difference is that WASM started with unsafe languages that have all of those features, so the developers are aware of them, and now they're trying add safety.
Prior universal VMs started with a safe language and bytecode and then added unsafety. Maybe that will change the outcome, maybe not. At the very least you can still use conservative GC.
I can see value in running code on multiple browsers on multiple OSes via web pages. I’d hate to npm install 2k+ JS libraries I don’t know or understand to be able to compile a simple Hello World Kotlin program that runs on a webpage. There does seem to be some notion in the industry that everything should be in a browser/JS, sigh.
To run a wasm-compiled program in a webpage you need maybe 30 lines of JS total. Maybe a 10 more lines if your application wants to draw on the page through a canvas
It's a bit more complex than that, JS and WASM have a huge impedance mismatch (if you can call it that) because they can only communicate via shared memory and functions calls using numeric types as arguments.
As soon as you want to pass a string, struct or arbitrary map between JS and WASM you'll find yourself in a world of pain that comes from writing bindings that de/serialize data on both sides. Rust seems to have a somewhat capable bindings generator but the core issue still remains in any other language targeting WASM.
I'm not keeping myself up to date, but as far as I remember WASM/JS interface types proposal was supposed to address this issue. Has there been any progress on that front?
> To run a wasm-compiled program in a webpage you need maybe 30 lines of JS total. Maybe a 10 more lines if your application wants to draw on the page through a canvas
If you read more carefully you will see that I mean that if your application is completely written in WASM with no JS interop all you need is some JS glue code to load the WASM binary
If you want to manipulate the browser DOM from inside the WASM binary then yes, you need a lot of JS
> I'm not keeping myself up to date, but as far as I remember WASM/JS interface types proposal was supposed to address this issue. Has there been any progress on that front?
Yeah, I don't know how you could have one gc that works for everything unless that gc was very tunable/configurable and you could have different profiles for each language. Maybe the program payload includes the gc tuning parameters such that the gc performs like the GC for the program's host language?
Isn't the JVM kinda of a universal GC? Putting the different GCs in the JVM itself aside, a lot of other languages actually have runtimes for Java and work very well, used in production and all. Jython, JRuby, JS engines (Rhino/Nashorn), Groovy, etc.
Not quite, and this has had security implications. These are known as "full abstraction" failures, where JVM bytecodes can violate guarantees made by a language running on top of the JVM. The Joe-E language is basically a security-oriented subset of Java, and they had to explicitly decompile JVM bytecode of any code they're using, then recompile it in order to preserve full abstraction and avoid introducing vulnerabilities.
This is from the author of TeaVM, who has 10 years of experience getting Java and JVM code to run efficiently in the browser. https://teavm.org/
TeaVM's existing transpilation of Java to JavaScript performs well (using the browsers JS GC). It will be interesting to see if WASM GC matures to the point where it is even faster.
* Null checks: Mostly fixed by WasmGC. The spec defines non-nullable local types, and VMs can use the techniques the article mentions to optimize them using signals (Wizard does, for example).
* Class initialization: This is a difficult problem, as the article says. J2Wasm and Binaryen are working to optimize it through static analysis at the toolchain level. Here is a recent PR I wrote that makes progress there: https://github.com/WebAssembly/binaryen/pull/6061
* The vtable overhead issue the article mentions may be a problem. I'm not aware of good measurements on it, through. There are some ideas on post-MVP solutions for method dispatch that might help, but nothing concrete yet.
* Checks for null and trapping: There has been discussion of variants on the GC instructions that throw instead of trap. Measurements, however, have not shown it to be a big problem atm, so it is low priority.
The author is right that stack walking, signals, and memory control are important areas that could help here.
Overall with WasmGC and exceptions we are in a pretty good place for Java as emitted by J2Wasm today: it is usually faster than J2CL which compiles Java to JavaScript. But there is definitely room for improvement.
I feel like WebAssembly is becoming more and more like something like the .NET CLR - a virtual machine designed for running high-level languages, rather than a virtual CPU architecture.
Continuing with this analogy, that once all the building block are in place (GC, threads etc.), it would be time for building a WebAssembly-native programming language.
The wasm security model is a lot more complex than your CPU's. It was never a virtual CPI architecture.
And because it's not a virtual CPU architecture, it need those extra complexities. Your CPU is designed for running high-level languages without any of those extra features, but wasm can't do it (well enough).
I think you could really speed up WASM a lot if CPUs supported the WASM sandbox better. For example, it would be nice to have modern segmented memory support where you could set an offset and max address such that all other pointer operations worked off that mini-TLB, generating an interrupt if the bounds are bypassed.
More complex designs tend to go for a full-blown MMU, and I wonder if the presence of such a feature would be warranted when you could go for either full-blown process isolation (which afaik is not that expensive on modern CPUs), or just go with static and dynamic checks (basically and if statement that checks if the address is valid, which can be optimized away like 90% of the time, when iterating through known arrays etc.)
The part that would be nice to bypass is TLB switching and cache invalidation. WASM doesn't need full page translation because it largely assumes the containing process already does that and because it doesn't support allocating non-contiguous memory without using multiple linear memories. Even with multiple linear memories, it still doesn't require (or event want) page translation because these memories each have their own address space.
The issue with if statements are that the stats/bits branch predictors use are a finite resource. You really need these checks to be inlined as well because otherwise you'll thrash the instruction cache. If it just had some special register with an interrupt, the CPU could just always assume the index is valid for the purposes of speculative execution and branch prediction.
This is sorta what plenty runtimes do, but on a larger scale with pages with incorrect permissions. I don’t think that interrupts going through the OS would be feasibly fast for scales at typical array sizes, probably a properly branch-predicted conditional will be faster.
WASM has a very simple security model compared to almost any modern CPU. No processor has a linear memory map with no segmentation or permissions these days.
I was under the impression that direct interaction with the DOM from WASM was predicated on GC support. Does this mean that something like python could now directly access the DOM without javascript?
Yeah I think the component model is biting off way too much in one proposal. All the future WASI developments are gated on it too now so we won’t see any improvements there either until this all-singing, all-dancing universal interop is shipped.
To be fair, if it takes this long to get it right, than I agree with the slow pace. Look at applets. We don't want that security nightmare again, so I imagine a very slow pace is necessitated.
I really do prefer open stuff so I have zero plans on turning wasm on in my browser. How does open web benefit from wasm, pretty please? Because open web is top priority, not enabling astonishingly complicated, compiled and locked down "apps". Did I miss something?
Maybe you mean "openess" in an "in-browser inspectable" sense, ie. "open web" as in "Gopher & Gemini", but if you mean "open" as in "open-source" then there actually some folks doing really interesting things in this space:
I don't think you are missing anything, it's just that most people care more about easily building things than "openness".
Do you also not run javascript code of websites that has been run through a transpiler with tree-shaking and minification? Because WASM is about as locked down as that.
Since unfortunately the proliferation of use of WASM on the open web seems inevitable (to me), perhaps some community effort into tooling to automatically inspect and disrupt WASM apps to make your browser behave is worthwhile. Random ideas: web canvas OCR scrapers, dumping longer strings from wasm memory to render a useful page, etc. I'm thinking of the regular debug tools but on steroids and geared around subverting the user-hostile stuff we're about to see.
I'm less interested in WASM in-browser than I am for WASM-powered services in edge computing (Cloudflare workers, Wasmer Edge, etc). Without WASM, you're always going to suffer cold start issues due to containers or other runtime initialization costs. This is likely the reason why Cloudflare Workers are so much cheaper than Lambda, however it's limited to workers/wasm.
Binaries require a container or VM sandboxing in order to run. Java bytecode, afaik, has a slow cold start issue and still requires some additional sandboxing afaik. WASM can cold start fast because the runtime API is natively restrictive to local resources and requires explicit permissions to do otherwise. It would seem that WASM actually makes for a better binary format for edge computing.
Since the WASM runtime doesn't have to worry about resource access violations or memory corruption, it doesn't need a VM or container. This means WASM can start faster without needing any additional security wrappers. I'm doing my best to simplify, but I'd recommend reading about Workerd or Wasmer to learn more about WASM host services.
With all due respect, this doesn’t make any sense. A VM in the JVM sense is an almost identical runtime - why would one start faster than the other? The only difference might be between the speed of the interpreters, as WASM is a bit lower-level than JVM byte code.
Containers have absolutely nothing to do with the topic, and I’m afraid if you mix them up with the former kind of “VM”s your point is moot.
I know little about JVM, but I've gathered that WASM cold starts are far quicker because WASM can be incrementally compiled* as bytes are streamed into edge services. Cold start time is king when almost every user is using a different edge location.
* https://stackoverflow.com/a/58134568
This was a very enlightening thread. My takeaway is: It actually is a similar approach to the jvm, but cold starts faster, whether because of differing design and implementation choices explicitly made to achieve this, or just because it ended up working out that way.
But in either case, "a bytecode for a JIT-compiling runtime that cold starts very quickly", does indeed sound very useful for edge computing. Thanks!
Ultimately, you probably won't have an option - just as one can't disable JavaScript today and still view most of the web. If the vast majority of people leave WASM enabled, companies will build things using it. It seems likely that WASM will displace JavaScript over the next 20 years as engineers decide they'd like to use non-JS languages to build their client-side code. It will take time since there's a huge amount of JS that's already out there, but new projects will become less and less likely to choose JS. Some still will. Many people like JS. However, I think a majority of new front-end projects 10 years from now won't be JS.
I'm hopeful there is a future where the browser can use a wasm binary as an entry point for a website (rather than an html file). This implies access to the DOM and other web platform APIs.
Obviously this is terrible for document websites that need to be SEO optimised, however it would be great for dynamic web applications (think banking apps or Jira).
In those cases, the HTML file is just an unnecessary extra request and can actually contribute to slightly slower start up times.
Perhaps simply allowing html files to be sent in some kind of binary format where a wasm binary is inlined would be a more practical approach - as the html file does act as a kind of app manifest.
To the end developer it probably seems so but to the browser it's very different. Java Applets were things plumbed through the browser whereas WASM was designed as an expansion of the existing browser engines. What they enable one to do may seem very similar but WASM does it in a way that is significantly better (security, execution, maintenance, integration) for the browsing world.
I am not a fan of web assembly as a general target for web apps. I think something like this makes sense for low-level performance-critical libraries, but not much beyond.
I've been using Blazor for some time now (server-side mode), and I feel like even this part without WASM is overkill complexity for 99% of the target audience. Adding the client-side WASM piece just kicks it into another dimension of "hard to reason with".
I wish we could just exchange write-only or readonly byte buffers between WASM guests and their hosts using a sane stable API. Sadly, I no longer trust the standards folks to get something like structured data exchange (interfaces, components) to ever work correctly.
Can someone explain if this could make a potential statically-typed javascript easier to standardize?
>WasmGC allows you to define struct and array types and perform operations such as create instances of them, read from and write to fields, cast between types, etc.
Would it make sense to define javascript types within this existing wasm standard? I imagine V8 is basically already doing this as wasmGC is just exposing the existing js GC, right? (Not sure aboout other browsers.) Even if we don't get static types in browsers it would be great if they could be defined so that an outside implementation could make use of it.
> Wasm is a low-level compiler target and so it is not surprising that the traditional porting approach can be used
Stack machines often cannot be targeted the same way as register machines; in addition, the fact that the call-stack doesn't share address space with the heap causes challenges.
Someone who's well versed can calrify that because Go is a language with garbage collection so does it mean it is not as suitable for targeting web assembly?
Go exposes interior pointers to user code, so it will have some extra difficulty fitting into WASM GC’s current limitations. Similarly some of .NET’s recent GC features will probably clash with this interface. Honestly I think only languages with very simple GC semantics (e.g. JVM languages, scripting languages) will get much mileage out of it as currently specified.
I would characterize it more as Go is not a great language for WASM because of its relatively heavyweight runtime and a simple compiler that isn't focused on making small binaries (AIUI, no "tree shaking" whatsoever, once a module comes in you get 100% of it unconditionally) makes fairly large WASM binaries. The runtime model mostly works in WASM today but it has a few roughish edges that can get you in trouble (though similar rough edges are in many languages at the moment).
If Go could depend on WASM having a GC, and if Go doesn't have some sort of critical semantic mismatch with what WASM implements... and it is shocking how small the mismatch has to be before it becomes a critical problem sometimes... then the Go WASM binaries could get smaller by not needing to ship a full GC. However, it's still going to be pretty large even so.
It's probably a critical feature for Go to be able to go WASM and in the long term be able to use WASM-based code, because it's often important for it to at least be an option. But I seriously doubt any WASM-first project in the next several years is ever going to pick Go as its base. You start with an awful lot of compromises right off the bat.
If WASM succeeds, it will slowly but surely start to escape the browser. When you're not in a browser anymore, and you have something like a WASM executable in hand that you want to run in some WASM environment for some reason, the tradeoffs get less bad, for much the same reason it is usually not that big a deal that Go executables can be a bit on the large side right now. But this is still years off. WASM's immediate future is in the browser, where the extra large runtime and build sizes is not necessarily a fatal objection, but a fairly significant cost right up front compared to a lot of alternatives.
This is off-topic, but we're always looking for compiler people at PRQL (https://prql-lang.org/) to help us build a query language for the next 50 years.
Ok, perhaps I'm using the wrong terminology, but what I meant is that the GC can collect garbage from multiple threads at the same time, while the GC itself also runs in a different thread (or multiple).
Without this, you can't really run languages like Go or C# inside the VM.
For an example of a platform that uses this feature, Spritely's Guile-on-Wasm project, Hoot, is now runnable in Chrome and Firefox. See https://davexunit.itch.io/strigoform for an example.
Js has evolved since the initial version, which also absorbed the knowledde from other existing languages.
But my main point is the years and years of excellent engeneering in js implementation. Languages compiling to js can utilize its gc as well as many optimizatoins js engines such as v8 provide.
For example, if your language allows to dinamically create key-val objects, the language could automatically benefit from hidden classes v8 creates. Implementing the same in wasm would be much more difficult.
I think WASM is an example of a thin waist [1] with its garbage collector and N+M rather than N×M. (N languages + M virtual machines + G garbage collectors). That's a mature garbage collector in V8.
I was curious if there was a WASM to JVM and it seems there is one on GitHub, I haven't used it I was just curious because the JVM is a mature (and parallel) garbage collector.
Now I'm excited for WASM Threads for true parallelism and not just IO parallelism because I didn't think WASMGC would come out so soon.
The opportunity to solve async and parallelism and garbage collection EFFECTIVELY would strengthen WASM and not be a source of confusion or difficulty for developers. I think that's why WASI is so important, a chance to define an API as stable as POSIX.
1: https://www.oilshell.org/blog/2022/02/diagrams.html
2: https://github.com/cretz/asmble