HN user

drogus

634 karma
Posts5
Comments128
View on HN

I think it's a mix of people being actually hyped and wishing this is the future. For me, productivity gains are mostly in areas where I don't have expertise (but the downside, of course, is I don't learn much if I let AI do the work) or when I know it's a throwaway thing and I absolutely don't care about the quality. For example, I'm bedtime reading a series of books for my daughter, and one of them doesn't have a Polish translation, and the Polish publisher stopped working with the author. I vibe coded an app that will extract an epub, translate each of the chapters, and package it back to an epub, with a few features like: saving the translations in sqlite, so the translation can be stopped and resumed, ability to edit translations, add custom instructions etc. It's only ~1000 lines of Rust code, but Claude generated it when I was doing dinner (I just checked progress and prompted next steps every few minutes). I can guarantee that it would take me at least an evening of coding, probably debugging problems along the way, to make it work. So while I know it's limited in a way it still lacks in certain scenarios (novel code in niche technology, very big projects etc), it is kinda game changer in other scenarios. It lets me do small tools that I just wouldn't have time to do otherwise.

So I guess what I'm saying is, even with all the limitations, I kinda understand the hype. That said, I think some people may indeed exaggerate LLMs capabilities, unless they actually know some secret recipe to make them do all those awesome hyped things (but then I would love to see that).

you have to understand almost all of the language very intimately to be a productive programmer,

I've seen absolute Rust noobs write production code in Rust, I have no idea where did you get that notion from. Most of the apps I've written or I've worked with don't even need to use explicit lifetimes at all. If you don't need absolute performance with almost none memory allocations, it's honestly not rocket science. Even more so if you're writing web backends. Then the code doesn't really differ that much from Go.

Worker threads can't handle I/O, so a single process Node.js app will still have the connection limit much lower than languages where you can handle I/O on multiple threads. Obviously, the second thing you mention, ie. multiple processes, "solves" this problem, but at a cost of running more than one process. In case of web apps it probably doesn't matter too much (although it can hurt performance, especially if you cache stuff in memory), but there are things where it just isn't a good trade-off.

I love WASM and WASI, but it's not nearly the same, unfortunately. Performance takes a hit, you can't use async in a straightforward way, launching hundreds of thousands of tasks is problematic etc. WASM is great for allowing to extend your app, but I don't see it as a replacement for an ABI anytime soon

Claude 4 1 year ago

In my experience it really depends on the situation. For stable APIs that have been around for years, sure, it doesn't really matter that much. But if you try to use a library that had significant changes after the cutoff, the models tend to do things the old way, even if you provide a link to examples with new code.

While I agree with the sentiment here, ie. that Ruby doesn't necessarily need namespaces, I think it's also not necessarily good to base Ruby usage on what Shopify is doing. They have so many expert Ruby devs, and whole teams that write extra tooling, that I'd argue they shouldn't be compared to pretty much most of the usage of Ruby/Rails out there

Have you ever written a web app in Rust? Most of the code is in a form of handlers that receive data, process the data and give some data back. There is rarely need to think about lifetimes or borrowing in these scenarios.

"the community" meaning one weirdo commenter you've seen on HN? Cause I can assure you no people I know in the Rust community think that way.

Personally I would really like to code some stuff in Zig if I had more time. It's not really appealing to me in many ways (like I prefer to spend a bit more on designing types for my programs and have safety guartantees), so I wouldn't probably ue it long term, but I admit stuff like comptime is interesting.

At some point there was a discussion about compile time reflection, which I guess could include functionality like that, but I think the topic died along with some kind of drama around it. Quite a bummer, cause things like serde would have been so much easier to imeplement with compile time reflection

Hard to say at this point, but I really doubt it will be ever faster than SpiderMonkey or V8 with JIT enabled. Modern JavaScript compilers are quite good at optimizing hot paths through JIT. The use case of this project is to be able to run JavaScript in WebAssembly sandboxed environment. Like, for example Shopify allows you to extend their backend code using WebAssembly, but there is a binary limit capped at 250KBs. At that size you can't really use JavaScript at the moment, cause even simple interpreters like QuickJS take a few MBs compiled to WASM.

Yup, that's something that I would definitely like to do. In performance critical applications adding type information can speed up things quite a lot, especially when you think about numbers. At the moment I have to pass numbers as structs with one f64 field, because an argument to a function in Javascript can be any available type. If the type is specified, the signature or return type can use f64 types directly and don't even do heap allocations. But even for heap allocated types it may result in better performance, cause at the moment for each object I have to check what type it is and what path to take in order to proceed.

I haven't seen Wasmnizer before. This looks like a cool project and I'll be definitely taking a closer look when I have some time, but for now I just took a quick peek.

The main difference is obviously TypeScript vs JavaScript. Because they are not bothering with JavaScript, some stuff is much easier, like for example if a function specifies an argument as `number`, you can statically compile it to use a type representing a number (possibly even without heap allocations) instead of passing something like `anyref` everywhere and doing typechecks.

The second big difference is that they seem to use types imported form the host. This means they can implement part of the types/builtins/methods on the host (which is easier, cause in JavaScript you simply use JavaScript for defining host stuff) at a cost of doing more calls from WASM to the host. And then you need the host to implement those functions (here is an example for JS host: https://github.com/web-devkits/Wasmnizer-ts/blob/main/tools/...). My plan for Jaws is to only rely on WASI, so that you can run your program on any runtime that supports WASI.

I'm also not sure about their approach to semantics. I can't quite grasp how they implement for example scopes. I tried to compile a small TS script that relies on scope, like:

  let message: string = 'Hello, World!';

  function test() {
    console.log(message);
  }

  test()
And the result was console.log printing null. I'm not sure if I'm doing something wrong or is it expected, but I'll ask them in an issue later.

I haven't seen Static Hermes before, I'll take a look, thanks for the link!

With regards to porffor. It's a very good project and people behind it are probably much better at writing interpreters/compilers, but the biggest difference is in what WASM features the projects use. porffor uses only core WASM, which means they have to implement a lot more features themselves. Data types like arrays, structs/objects, garbage collection, exception handling etc. I am using WASM features added in proposals standardized very recently, like WASM GC or exception handling, which means I get a lot of the features almost for free. And I suppose that's also why semantics like scopes/closures are really hard to do for projects like porffor or even AssemblyScript and were relatively easy in Jaws.

The trade off is mainly in support. porffor compiled binaries can run on a lot of different WASM runtimes, I think there are even some runtimes for Core WASM for embedded devices. In case of Jaws, the runtime needs to support the proposals I use. Currently there are only two runtimes, that I know of, supporting both WASM GC and exception handling: V8 and WasmEdge. I believe more runtimes will get there, like for example WasmTime people are working towards exception handling, but it will take some time. Which is not a problem for me, cause it will definitely take a bit to reach any production level JS compatibility - I think the runtimes will have time to catch up with the proposals by then.

Hyped and visible on socials doesn't necessarily mean it's widely used, but I guess it also depends on how you define widely. According to most stats I've seen Rust usage is maybe about 5-10% of languages like JavaScript and Python when you look at StackOverflow, indexes like PyPl, or GitHub statistics.

thanks for reporting and sorry about that! I was prepending some JavaScript code to be compiled for test262 harness before, but at the moment it was just an empty file, so I removed it and pushed a fix. Please try again now and let me know if you run into any issues!

Yeah, at the moment it's passing about 12% of tests, but there is a lot of low hanging fruits to implement, especially considering I started the project two weeks ago. This doesn't mean I will hit 100% in linear time, unfortunately, cause there is a long tail of builtin types and functions, but as I started by implementing the "hard parts" I left some easy parts not done. For example I implemented only enough syntax to allow running conditionals and a while loop cause it was needed for the test262 harness, but I left all the other loops (for, for in, for of, do while) and conditional expressions (switch) unimplemented. Implementing them will be more or less analogous to existing implementations for if/else and while.

Once I finish implementing `await` and generators, which are the last hard to implement semantic concepts, I will be implementing those low hanging fruits. It's hard to say how much coverage that will give me, but just to give an example: currently 1200 tests fails, cause `object["foo"]` syntax is not implemented. Ie. `object.foo` works, but `object["foo"]` does not. It doesn't mean that those 1200 tests automatically will pass, cause they might be testing other stuff, but there is a lot of such relatively simple syntax ommissions that make hundreds of tests fail.

And yes, I would love to have a nice graph like porffor has! :D

Not yet, it's very early stage where I'm mostly implementing full JavaScript semantics (and hopefully finding some funding/support for the project), but as soon as I'm done with async/await and a few simpler missing pieces, I will start implementing JS builtin types

I know about Jaws as a screen reader, but I don't think it is much of a conflict. WebAssembly is a niche within a niche (like a tool in a WebAssembly ecosystem within a programming ecosystem), so it will probably never grow to a popularity where it's higher in search results making life of people using screen readers worse. But even now, when you enter Jaws into google now, the whole first page is about Jaws, the movie. So if anyone wants to find either of the projects by name they have to specify it more anyway, just like you often have to do with other projects (for example I always search for "phoenix elixir" instead of just Phoenix). My take is that with the amount of tools and programs there is no way to use simple names and not have a conflict here or there.

These are very good questions, I'll respond here, but I'll also add more info to the README. This project is mainly targeting WebAssembly usage on the server, cause I think it makes little sense to run JavaScript in WebAssembly in JavaScript (although time will tell, maybe it will be useful for sandboxing frontend plugins?). Regardless if it's running in the browser or a backend runtime like WasmTime or WasmEdge, at the moment running JavaScript inside WebAssembly is not ideal. You either have to compile a JS engine like V8 or SpiderMonkey to WASM and then use it to run your script or you have to settle for an "almost JavaScript" language like AssemblyScript. This is a limiting factor for running server workloads. For example Fastly uses SpiderMonkey for their WASM workers, but it means that each instance uses 5-10MBs of memory even for a hello world. Shopify, on the other hand, uses WASM for customizing server side of their shops, and they decided they only allow WASM binaries up to 250KBs, which is a no-go for embedding any interpreter. Thus their "blessed" language is AssemblyScript. They outline reasons for that here: https://shopify.engineering/shopify-webassembly

This is all due to a fact that historically WASM was a very simple runtime. It was relatively easy to compile C code to WASM, just like you compile C code to machine code, but even though a WebAssembly is a kind of interpreter by itself, it wasn't easy to interpret higher level languages on top of it.

With new proposals being standardized, like garbage collection support or exception handling support, WebAssembly becomes much more powerful interpreter, with stuff like structs, arrays, function references etc.

Jaws leverages that fact translating JS code to WASM code in a way that WASM interprets the resulting code, without the need of a JS engine like SpiderMonkey. In practice it mainly means that a binary generated by Jaws will be probably under 50KBs vs 10MBs when you compile SpiderMonkey to WASM and run your script on top of that. Memory usage will be also significantly lower. For companies like Fastly this would mean orders of magnitude lower memory usage and thus server costs. For companies like Shopify it would mean they could leverage JavaScript code already available (think NPM packkages) and JavaScript ecosystem for people writing plugins for Shopify's backend.

is it intended to be a tool compatible with browsers and other WASM runtimes or is it only compatible with a runtime linked to the project

The only runtime the project uses is WebAssembly. The generated code is mostly 3k lines of WAT code form this file: https://github.com/drogus/jaws/blob/main/src/wat/template.wa... and whatever your JS code is translated to. For example for a very simple program like "console.log('foo')" the entire "generated" part is this: https://gist.github.com/drogus/1c49c25ed0b14804b2f27e10d2a79..., which more or less prepares an argument (with new_static_string) and then calls console.log. Right now I need a bit of glue code on the host, but eventually it will be possible to execute such a binary with any runtime that supports WASIp2, WASM GC and exception handling proposals.

Somewhat linked questions: How does it react if it encounters e.g. web APIs inside the JavaScript code or other global identifiers only defined in some environment (e.g. a recent browser, Node.js etc.)? Or if it's not intended for those environments, how are you supposed to do I/O when using this?

None of this is implemented yet, but I can tell you how it will work. I plan to support Node.js APIs through WASI. WASI is a standard for communicating between WASM programs and the outside world. For example WASI defines a standard set of functions you can use to send an HTTP request, or write to STDOUT, or read/write to a file. So when I get to APIs like `fetch` or `fs`, it should work with any runtime that supports WASI preview2. Browsers could also be supported with polyfills, but in this case I/O support is more custom. Like, if you decide you allow WASM programs to write or read files, you would have to provide a mechanism to do that, for example save files to localStorage or an SQLite database compiled to WASM (or I guess even send them to S3 or something along the lines).