0 - https://github.com/microsoft/verona/blob/master/docs/explore...
0 - https://github.com/microsoft/verona/blob/master/docs/explore...
The rest of these applications have either hard realtime or hard performance requirements or both. What I don't see in these apps, is for memory safety issues to be 'ok'.* It's 2020, using a memory safe language should be table stakes for new projects at this point.
----------
* The one exception I can think of is single player video games where the worst that can happen is basically just a crash to desktop. Multiplayer games are a different story especially with the trend toward paid DLC where these kinds of issues could result in anything from exploits in multiplayer matches (hurting the community and thus the game overall) or players circumventing DLC restrictions (resulting in a loss of profit).
I don't know if explicit regions help very much with safety, but you can certainly go places with a more expressive type language. E.G. ats - http://www.ats-lang.org/
Rust is pretty nice, but the options for allocation and it's ownership type system make it a mismatch for certain types of applications (probably games and OS's but we'll see how that plays out).
I think regions are neat. And I'm looking for an opportunity both for playing around with it myself and for the programming language community at large getting more exposure to them.
More ideas exposed to more people leads to better ideas of how to fit everything together and how to leverage what features to solve some problems better.
The good news is that whatever issue you have can probably be solved without resorting to FFI. There are plenty of other interop options - for example, if you control the library, you can use your operating system's IPC mechanisms with an agreed-upon serialization format. Or, if your library and application are within the same ecosystem, you can use language-specific library management features such as Python's import or Rust's crates. FFI is a bit like C itself - if you're reaching for it as a solution to your problem, then your finger is already on the trigger of the footgun.
So from this perspective, the interop target should actually not be the lowest common denominator, but the opposite: a target that can describe all possible user languages in a common format.
In fact, I would actually argue that the implementation details are NOT that interesting and only a distraction: that we need an abstract way of declaring compatibility. For instance, one can imagine a high-level FFI target where function types are parameterized by their calling convention i.e.:
`foo: (VOID<C>->VOID<C>)<C>`
`bar: (UNIT<Rust> -> UNIT<RUST>)<RUST>`
and attempting to call `bar` from `foo` involves a compiler intrinsic like `RUST_CALL(bar)()` to convert the type (and the calling convention) appropriately.
If you have code in Ruby and in Julia that you want to mix together it's probably because both pieces of code leverage the distinct capabilities of those two languages, which have nice capabilities because they accepted very tradeoffs.
Would you really want Julia with Ruby's Number class? It would entirely kill performance. You want it to be an integer maybe, or a double, or sometimes something else. It's going to be application specific is the point, because another user want some different subset of Number's behavior. I don't think there's a shortcut here besides making a very limited or opinionated FFI system.
I don't think that's doable unless the underlying language already has the same ownership and lifetime guarantees as Rust does, otherwise these bindings have to protect against memory issues by effectively taking ownership of the data to garantee its lifetime, which is not zero cost.
I simply don't think that you can auto-generate zero-cost safe C or C++ bindings automatically.
As stated, that's likely an accurate statement, but if you separate the requirements for the bindings the problem looks way more tractable:
- auto-generate: I'm pretty positive that this point is only hard given the following, and I feel you would agree with that assessment, so I'll just focus on the other ones.
- zero-cost: doing zero-cost ffi is the best option, but a lot of advanced constructs that people want to use cannot be well represented in that way because they rely on the internal consistency of the host language. That means that dynamically loading a library written in Rust might make it so that you can only interact with ADTs, trait objects and whitelisted provided traits on those ADTs, potentially only through trait objects. That's not zero-cost, but it is super powerful. ObjectiveC has lived its entire existence going through vtables and that has even enabled runtime introspection that was super useful.
- safe: safety can be accomplished easily enough if you don't constrain yourself to zero-cost or if you put constraints on what is supported for zero-cost ffi. You could say that method calls on trait objects is safe, but type parameters are out of scope because ffi and monomorphization are not compatible. You could restrict yourself to either no borrows crossing ffi, or no mutable data (only internal mutability through trait methods) allowed.
- C or C++: I would focus on other languages, personally. Having easy interop with Java, C#, Python and JavaScript would open opportunities that do not have anything to do with systems programming per-se, but that would make it more likely that the next numpy isn't written in pure C.
- automatically: the need for extra annotations in either end of the interop simplifies part of the problem and might even help with documentation. Adding some light ownership information to APIs in non-Rust languages would be great for non-Rust users of those APIs.
But I think it's inevitable you have to do things on both sides of the language boundary, i.e. pushing them towards a lowest common denominator. (e.g. Microsoft's COM, which actually worked pretty well)
Really what it pushes you toward is wire protocols and NOT relying on the type system. That is the C and C++ ABIs are related to but different than the APIs (type system).
My experience tells me that transparent interop is kind of a pipe dream. The problems always pop up somewhere. By "transparent", I meant "Rust function calls C++ function" and "C++ function calls Rust function" without other metadata/bindings. The codegen becomes a big problem in practice.
Fundamentally a lot of languages LOOK the same but they ACT completely differently. A Rust function is not a C++ function is not a Python function is not a Go function. (And funny thing -- as of C++ 11, C++ now has many different notions of "function", because of move semantics).
And functions are actually "easy" compared to types (e.g. inheritance vs. typeclasses vs. interfaces)
----
Basically I would say the problem is that you either spend time manually wrapping or annotating your code (as you say), OR you put an ever growing list of heuristics in the code generator, a la SWIG.
Those heuristics have bugs, and will make your program unreliable. They're also "someone else's problem", which leads app developers to come up with horrific workarounds.
So I go with the simple manual wrapping, and reducing the number of things to wrap by changing the structure of your program. This also has other benefits like efficiency, i.e. crossing the language boundary less often.
(Related: I think better build systems can go along way toward solving this problem. Unfortuately there seem to be a lot of language-specific build systems and package managers now, which only exacerbates the interop problem. If you have one language-neutral build system, it's not that bad.)
https://dconf.org/2020/online/index.html#schedule
https://dlang.org/blog/2020/11/20/dconf-online-2020-how-to-p...
It's free to participate!
What Vale does that is attractive is to make simplifying assumptions that address this problem domain closer to the level of granularity that it deserves.
We'll see as things continue to develop here.
I think Rust for core engine and Lua for game logic would be a pretty solid combo.
It’d be super if regions were a mathematically robust way of describing that engineering solution.
Note that my knowledge of regions comes mainly from Cyclone, not Vale or Verona, are those different concepts with the same name?
Is my intuition correct that one could implement this type of memory handling in Rust as well?