8 ms·
Roto: A Compiled Scripting Language for Rust
- 90s_dev 1y ago> Finally, we want a language that is easy to pick up; it should feel like a statically typed version of scripting languages you're used to. It looks like Rust. All Rust scripting languages do. Is this true for all other languages? Is this just a property of embeddable scripting languages, they will always resemble the language they're implemented in and meant to be embedded in?
- pansa2 1y agoIIRC Lua deliberately doesn’t resemble C - so if you’re going back-and-forth, editing both the host application code and a script, you can immediately tell which one you’re looking at. Makes sense to me - which means scripting languages for curly-brace languages should probably use either Lua-style begin-end or Python-style significant indentation.
- 90s_dev 1y agoI don't think that's the reason behind Lua's syntax. I think it was designed specifically to be extraordinarily unambiguous, clean, and simple, which it is.
- ordu 1y agoIt is one of my issues with lua. I'm starting to forget semicolons in C/C++/Rust and write : instead of ::. I think, that I'd better deal with a language recognition, I could configure emacs to use different highlighting for different languages. Or I could change background color for buffers based on a language.
- andsoitis 1y ago> Is this just a property of embeddable scripting languages, they will always resemble the language they're implemented in and meant to be embedded in? No. Think about Lua or Tcl (both implemented in C) or others like Embeddable Common Lisp.
- 90s_dev 1y agoTrue, but those were designed in the 80s or 90s. I guess I'm thinking of embedded scripting languages designed since ~2015.
- Philpax 1y agoPeople who become proficient in Rust generally enjoy the syntax, so they want to carry it across. (As someone proficient in Rust who has pondered their ideal scripting language, I would have done the same.) To your more general question: it depends. AngelScript [0] looks very much like C++, while others, like Lua, don't. It's really up to the designer's discretion. [0]: https://www.angelcode.com/angelscript/ https://www.angelcode.com/angelscript/, but https://angelscript.hazelight.se/ https://angelscript.hazelight.se/ has better examples of what it actually looks like in use
- axegon_ 1y ago> who has pondered their ideal scripting language Et tu, brute? :D
- nicoburns 1y agoI think it's just that a lot of people like Rust syntax, and there is a lot of demand for a Rust-like scripting language (Rust syntax is also very close to JavaScript/TypeScript syntax which many, many people are familiar with)
- epage 1y ago> It looks like Rust. All Rust scripting languages do. Not koto (https://koto.dev/ https://koto.dev/) which is one of the reasons I appreciate it. I want an embeddable language targeted at my users, rather than myself which I feel Rust-like ones do. I also want an embeddable language not tied to my implementation language so if I change one, I don't have to change both. Koto only supports Rust atm but I don't see why it couldn't be supported elsewhere.
- stirfish 1y agoRoto means "broken" in Spanish.
- diggan 1y agoJust add a "Huevos" prefix and a "S" at the end of the name, and suddenly you're thinking of delicious food instead.
- darccio 1y agoFor those who don't speak Spanish, "huevos rotos" are broken eggs (or scrambled eggs). Edit: I had to double check, because it seems that both translations are valid.
- diggan 1y agoAh, well, it's a dish, common name for it is "Huevos estrellados" in most places I think though. https://www.google.com/search?q=huevos+rotos&tbs=imgo:1&udm=2 https://www.google.com/search?q=huevos+rotos&tbs=imgo:1&udm=... Scrambled eggs would be "huevos revueltos" Edit: in Spain at least. YMMV for other Spanish-speaking countries.
- juanramos 1y agoEither that or an unfortunate accident
- 90s_dev 1y ago> Note that nothing in the script is run automatically when the script is loaded, as happens in many other scripting language. The host application decides which functions and filtermaps it extracts from the script and when to run them. So it's closer to something like C or C++, where it just defined stuff and you can choose what to use? I guess that's fine when there's no initialization for the script to do. Maybe in your domain that's never the case. But many languages end up adding static initialization as a first-class feature eventually.
- samuell 1y agoSidenote, but I wish it became the practice to explain abbreviations such as BGP, to make the post intelligible to people outside the field.
- jpc0 1y agoBorder Gateway Protocol But I don’t even think people in networking would say that, it is canonically BGP. This is kind of like complaining about the abbreviation HTML, sure yes it is Hypertext Markup Language but everyone knows it as HTML to the point that there are probably people that don’t know it’s an abbreviation.
- k__ 1y agoFair. I'm currently writing an article about Flowspec and had to comb a few RFCs to understand what's going on. I opened the Roto announcement and felt very smart, lol
- federiconafria 1y agoAdding to this, both for BGP or HTML if you don't already know what they mean, the "full form" does not really help.
- bryanlarsen 1y agoAgreed. There are almost certainly more people who know what "BGP" is than know what "Border Gateway Protocol" is. But it still would have been a favor to the original commenter to write "BGP (Border Gateway Protocol)". If you write just "BGP" you annoy the reader who doesn't know what it is; they'll think it's your fault. But if you write "BGP (Border Gateway Protocol)", they still won't know what it is but they won't be irritated at you anymore.
- jpc0 1y agoSomeone linked a document a few weeks ago that address the topic of abbreviations and when the abbreviation is the common form it is acceptable to just use the abbreviation. I don’t think anyone in their right mind would open every article with HTML(Hypertext Markup Language) and CSS( Cascading Style Sheets). For networking it would be the same with BGP/OSPF/RIP.
- airstrike 1y ago> Roto fills this niche for us. In short, it's a statically typed, JIT compiled, hot-reloadable, embedded scripting language. To get good performance, Roto scripts are compiled to machine code at runtime with the cranelift compiler backend. Statically typed, hot reloadable scripting? Sign me up.
- echelon 1y agoI have dreamed of this for Rust. It is literally _the_ killer app to have a fast scripting language that pairs with Rust well. With fast development cycles and a safe scripting language, Rust will find itself in every single programming niche. Server development, game development, desktop application development. If the WASM DOM bridge gets better, maybe even web development. Everything will open up. You can prototype in the scripting language, write mature application logic in the scripting language, and for the parts that matter, move them to Rust. I hope Roto can be that. There are lots of other Rust scripting languages that fall short. A good scripting language will have great Rust interop, similar syntax, and allow simple calling and passing of data structures. It shouldn't weaken Rust in any way.
- cess11 1y agoWhat does Rust have that other languages with these properties don't?
- deleted 1y ago[deleted]
- mikepurvis 1y agoOn the game engine side, probably not a lot until there's more ecosystem built up, but across the board you could look at the niches filled by the Java/Groovy and C/Lua pairings to get a pretty good sense of what's possible— it would be the tools stuff that would most excite me: build/CI/automation frameworks, that kind of thing. Alternatively, look at a project like ruff— hundreds of linter rules statically implemented in native Rust [1], maybe that would make more sense if the rules could be more tersely expressed as runtime-loadable roto scripts that have access to the Python AST and a toolkit of Rust-supplied functions for manipulating and inspecting it? [1]: https://github.com/astral-sh/ruff/tree/main/crates/ruff_linter/src/rules https://github.com/astral-sh/ruff/tree/main/crates/ruff_lint...
- lewisjoe 1y agoELI5: Why not use typescript and an embedded v8 engine or deno to run them? What kind of advantages will I miss if I go for typescript as an embedded language for my application? Also by using v8, I open up new possiblities via wasm (write in any lang and run it here?) Will be helpful if somebody enlighten me.
- airstrike 1y agoWhy would I want to bundle an entire JS runtime? And why do you think you need that for WASM? And personally I will go out of my way to not use TS/JS if I can
- ijustlovemath 1y agosounds like it's not fast enough for their use case. plus, have you ever tried to integrate v8 into a project? Deno is fine for building binaries, but to date doesn't really have good support out of the box for bundling a script into a library, which this application seems like it would need.
- duped 1y agoBe warned that V8 is a behemoth and adds 100+MB to your binary size, is quite slow to link, and is not practical to compile from source for most projects. If you want a lighter weight JS runtime there's quickjs, if you want WASM there's wasmtime. Personally I don't think it's a good choice for what it seems Roto is used for (mission critical code that never crashes) since TS doesn't have a sound typesystem and it's still very easy to fuck up without additional tooling around it.
- giancarlostoro 1y agoThere's also Duktape for just JS minus the WASM (at least I don't think they've implemented WASM yet). https://duktape.org/ https://duktape.org/
- ijustlovemath 1y agoI think QuickJS wins over Duktape for ES5 compliance, though it's been a few years since I was evaluating embedded JS. They're both extremely easy to integrate into an application, in contrast to V8
- lynndotpy 1y agoWoah, this looks awesome. One of my favorite things about writing Rust is that it's expression-oriented (i.e. almost everything is an expression), something you almost never see in non-functional languages. I was wondering if Roto is also expression-oriented?
- terts 1y agoHi! Author here. It is indeed expression-oriented, mostly following the same rules as Rust. If-else is an expression, for example.
- ijustlovemath 1y agoIn case the author comes in, I'm curious about how you designed the registration mechanism. We have a Python application that makes heavy use of decorators to give strong runtime introspection capabilities, and I've always wondered if the same could be done at or near compile time in an equivalent Rust context. I think learning about the drawbacks and boons of the designs you settled on would be really informative!
- terts 1y agoHi! So for Roto, our introspection needs are actually fairly limited. We only need the `TypeId`, the type name, the size and the alignment. which Rust can give us without any additional traits. It's not possible currently to - for example - access struct fields and enum variants. That is something that I plan to add, but that might require a crate like `bevy_reflect` or `facet`. Rust is giving me just enough information to pull the current version of. More powerful introspection/reflection is not possible without derive macros. If you're ok with derive macros though, you could look into the 2 crates I mentioned. Hope that answers your question!
- ijustlovemath 1y agoDid you go through many iterations on the API of the registration? If so, which designs did you disqualify and why?
- terts 1y agoNot many iterations, but a lot of head scratching was involved haha. I decided against using a trait and a derive macro because I wanted to avoid running into Rust's orphan rule. We have a crate called routecore where most of our types are declared and then a separate crate called Rotonda which uses those types and uses Roto. I wanted Rotonda to be able to register routecore types. That's also the downside of the current reflection crates; they require each type to have a derive macro.
- abendstolz 1y agoVery cool! But I prefer the wasmtime webassembly component model approach these days. Built a plugin system with that, which has one major upside in my book: No stringly function invocation. Instead of run_function("my-function-with-typo") I have a instantiated_plugin.my_function call, where I can be sure that if the plugin has been instantiated, it does have that function.
- 90s_dev 1y agoHow is that not also stringly typed?
- abendstolz 1y agomatch Plugin::instantiate_async(&mut store, &component, &linker).await { Ok(plugin) => { match plugin .plugin_guest_oncallback() .call_ontimedcallback(&mut store, &callback_name) .await { Ok(()) => debug!("Successfully called oncallback for {plugin_path:?}"), Err(e) => warn!("Failed to call oncallback for {plugin_path:?}: {e}"), } } Err(e) => { error!("Failed to call oncallback for {plugin_path:?}!: {e}"); } } See the "call_ontimedcallback"? It's not a string. The compiler ensures it exists on the Plugin type generated from the .wit file. If of course I put a wasm file in the plugin folder that doesn't adhere to that definition, that wasm file isn't considered a plugin.
- 90s_dev 1y agoAh, fair enough. So it is still stringly typed, it's just verified at compile time. Which I guess is true about all compiled functions ever.
- phickey 1y agoThanks for using wasmtime! I worked on the component bindings generator you’re using and it’s really nice to see it out in the wild. To elaborate a bit further: wasmtime ships a [bindings generator proc macro](https://docs.wasmtime.dev/api/wasmtime/component/macro.bindgen.html https://docs.wasmtime.dev/api/wasmtime/component/macro.bindg...) that takes a wit and emits all the code wasmtime requires to load a component and use it through those wit interfaces. It doesn’t just check the loaded component for the string names present: it also type checks that all of the types in the component match those given by the wit. So, when you call the export functions above, you can be quite sure all of the bindings for their arguments, and any functions and types they import, all match up to Rust types. And your component can be implemented in any language!
- ori_b 1y agoThis language looks a lot like Rust. Why not dlopen() a Rust shared library instead? The implementation would be about as complicated, but it would be a well known language that's fully integrated with a large library ecosystem, well defined build and package set, rather than some custom one-off thing with no ecosystem. Going your own way means your users have to re-invent the wheel for themselves every time. With Rust's safety, it's not even that bad to re-open and re-load the binary when it changes; the big footgun with dlopen is use-after-free.
- abendstolz 1y agoDon't rust shared libraries have the problem of no stable rust ABI? So you either use the C ABI or you use some crate to create a stable rust ABI, because otherwise a shared lib compiled with rust compiler 1.x.y on system a isn't guaranteed to work with the binary compiled on the same system with another compiler... or on another system with the same compiler version. Right?
- ori_b 1y agoYou'd have a plugin layer that hooks in the right places with a stable ABI on one end, and a native feeling interface on the other.
- terts 1y agoHi! Author here. There's a couple of reasons. First, this language is syntactically a lot like Rust but semantically quite different. It has no references for example. We're trying to keep it simpler than Rust for our users. Second, using Rust would require compiling the script with a Rust compiler, which you'd then have to install alongside the application. Roto can be fully compiled by the host application. I think your approach might be preferred when the application author is also the script author. For example, if you're a game developer who is using a script to quickly prototype a game.
- erlend_sh 1y agoAre you saying Roto has no ambitions to be suitable as a gamedev scripting language?
- ost-ing 1y agoAny chance of no_std support for embedded systems?
- terts 1y agoHi! Author here. Would be super cool, but while the compiled scripts would work without allocations, the compiler itself does a lot of allocations. So unfortunately I don't think I could make that work.
- dkarl 1y agoCould you write 80-100% of an application in this language? I'm wondering if it could be a good application language for Rust programmers who want to use the Rust ecosystem and have the option of writing parts of their application in Rust for extra performance, but who also want to experiment and iterate quickly, and who want a simpler, higher-level language for expressing business logic.
- 0cf8612b2e1e 1y agoThat’s my dream. Seamless FFI to Rust with all of the core in a dynamic scripting language. If/when you need to tighten up performance/types, can port more of the code to actual Rust.
- dkarl 1y agoThat's my dream, but statically typed. Let programmers gloss over all the memory management stuff that makes writing Rust painstaking and complex (put everything on the heap by default if necessary.) Trade off runtime performance for faster iteration and simpler code. Still enjoy static typing, the Rust ecosystem, and the option to write parts of your code in Rust for maximum performance.
- terts 1y agoHi! Author here. You could try but there are some fundamental limitations. The biggest limitation is that we don't have access to the full type system of Rust. I don't think we can ever support registering generic types (e.g. you can register `Vec<u32>` but not `Vec<T>`) and you don't have access to traits. So it would work if you can reduce your API to concrete types. Otherwise - apart from some missing features - you could probably write big chunks in Roto if you wanted to. You could also prototype things in Roto and then translate to Rust with some simplified types. Also you'd have to accept that it gets compiled at runtime.
- hannofcart 1y ago> Roto has no facilities to create loops. The reason for this is that scripts need to run only for a short time and should not slow down the application. [1] Wait, what?! Isn't that choice a bit extreme? There have been plenty of times in scripting where I've needed loops! Am I missing something here? [1] https://rotonda.docs.nlnetlabs.nl/en/stable/roto/00_introduction.html https://rotonda.docs.nlnetlabs.nl/en/stable/roto/00_introduc...
- terts 1y agoHi! That bit of the docs is a bit outdated. We're probably gonna make it optional. The reason for that choice was that the filters for Rotonda need to be very quick and don't really require loops as long as you can do `contains` checks on some lists for example. So it should probably say "Roto _in Rotonda_ does not have loops". Roto the language as a separate project can then have loops.
- dlahoda 1y agobosque and fluence aqua also do not have loops (one can loop there, but not in normal sence)
- breadchris 1y agoI like yaegi [1] for go because it is an interpreter for the go language (almost fully supported, generics need some love). The most important part for me is being able to keep all my language tooling when switching between interpreted/compiled code. Also, there is little needed distinction between what is going to be interpreted and compiled. Once you start including libraries it gets dicey and the need for including the libraries in the compiled part is necessary. There is also a blog post that comes along with it describing how it was built! [2] [1] https://github.com/traefik/yaegi https://github.com/traefik/yaegi [2] https://marc.vertes.org/yaegi-internals/ https://marc.vertes.org/yaegi-internals/
- deleted 1y ago[deleted]