What's some stuff you'd like to see in a higher level language that would fit the design nicely?
Are those things really hard to implement on top of WebAssembly?
1) Why have regions in the VM instead of compilers that target it?
SaberVM does its own analysis of the bytecode given to it to make sure its safe. That way, if you receive SaberVM bytecode, you don't have to trust it blindly. Wasm and the JVM both do this as well, for the same reason. A big difference from those projects, though, is that SaberVM's analysis includes memory safety, like Rust. For that, it uses regions (as well as Vale's system, which is a big inspiration). The JVM doesn't try to guarantee memory safety, and Wasm guarantees it by sandboxing the runtime from the rest of the computer. However, Wasm doesn't make any memory safety guarantees about the usage of memory within the Wasm runtime. Since SaberVM is trying to be a first-class execution environment like the JVM, instead of an embedded one like Wasm, it has to make guarantees about the usage of memory within the runtime, and therefore uses regions (in tandem with something like Vale's generational references).
2) I don’t really get the exceptions thing
The exceptions thing is meant to be a lightweight way of getting the reliability of Erlang's BEAM VM, using a common approach to exceptions in CPS-based compilation. Having a built-in notion of exceptions means that whenever an instruction fails, it can jump to the exception handler instead of crashing. That way parts of the program can be cleaned up and even restarted during the execution, instead of just crashing.
3) What features could a higher-level language have that would make the most of SaberVM’s unique characteristics?
A higher level language that compiles to SaberVM bytecode might give more control over memory management than existing functional languages. Things like support for stack allocation, arena allocation, in-place mutation of values that are used linearly, and unboxed, nonuniform memory representations of values. I might also use an actor system for concurrency, since the CPS style makes stackless coroutines trivial to implement and the exception system and parallelism instructions should be able to do a good enough job at something BEAM-like. Lastly, I would definitely surface the exception system as a feature of the language, for the sake of writing your own exception handlers.
4) Are those features really hard to implement in Wasm?
Unfortunately Wasm isn’t a great target for functional programming at the moment. It uses structured control flow as a big part of its security, with ifs and loops and whatnot. Functional languages, on the other hand, use recursion for loops, so they often use jumps for function calls, or else they’d likely run into stack overflows. Very recently, Wasm added “tail calls,” that is, when a function returns the result of another function, that function can use the stack space of the current function, essentially freeing that space early. Wasm added this mostly to support functional languages better, since it saves stack space quite a lot. I intend to write a transpiler from SaberVM to Wasm at some point in the near future, that uses the new tail call feature. But even then, functional languages use polymorphism for their security, not structured control flow, and there’s just a number of philosophical differences that make compiling to Wasm a pain for functional languages. And lastly, Wasm doesn’t have any way to do the things the BEAM is known for, namely, massive parallelism with extremely resilient concurrency. I know there are proposals on parallelism but given the fact that Wasm is meant to run in the browser first and foremost, this will never be a big goal for it.
SaberVM is intended to be what functional programmers wish Wasm was: a statically typed runtime system that could be run in the browser but isn’t primarily for that, which could functional languages could easily compile to in a way that preserves the sort of polymorphism-based security reasoning that functional programs depend on.
5) Conclusion :)
As you can see from my discussion of this stuff, these ideas all precede me. My region analysis is from a Krary, Walker, and Morrisett paper. I use something like generational references from Vale. I'm taking significant inspiration from the JVM, Wasm, and the BEAM. My only contribution here is figuring out how to put these pieces together into a tight, portable little system.
And in the interest of executing things without a VM, I'm thinking a lot about AOT-compiling the bytecode to native binaries as well.
What about the verification complexity? The JVM screwed this part up somewhat, from what I understand, while Wasm is carefully designed to be linear to parse and verify throughout. And complex type systems aren’t always trivial to even check types in.
> The JVM doesn't try to guarantee memory safety
Wait, what? I always thought that (barring JNI and so on) Java bytecode was completely memory-safe (having been intended to run mutually suspicious code in the first place), it’s just that this is accomplished at the cost of pervasive tracing GC and bounds checks everywhere.
This "deficiency" doesn't appear to have held back the JVM from being wildly successful. To what extent do you think it matters then?
F# in Webassembly via Bolero - https://fsbolero.io
How well do you think it succeeds in meeting functional programmers' expectations?
fyi, that was true for Wasm 1.0, but WasmGC defines managed memory structures (structs with typed fields, arrays with bounds checking, isorecursive subtyping, immutability, etc.) which provide fully verified memory safety.
[0]https://github.com/HigherOrderCO/HVM
Really interesting to see how new lang concepts and refinements keep popping up this last decade, between Vale, Gleam, Hylo, Austral...
Linear types really opened up lots of ways to improve memory management and compilation improvements.