Hi!
I just discovered Roc a few minutes ago so I don't know much about it besides what was mentioned in the Youtube video uploaded by Developer Voices. But I had a question that I was hoping someone here with more experience could answer.
At some point in the distant future I would like to make a video game that allows its users to write code to interact with the world. It will be in Rust via the Bevy engine. Does it make sense to use Roc for this scenario? There were some things mentioned in the video that made me think it might be great.
What I would need is something that can quickly execute and compile small pieces of code. it doesn't need to be as fast as Rust, but I don't want it to be orders of magnitude slower either. It would also be fun if the users could do this in an online multiplayer environment, so sandboxing would be critical.
edit: I realize that there are many other solutions I could look into, but I was wondering how easy/ergonomic it would be relative to those solutions. So it would be great to know of any difficulties or pitfalls with a possible Roc implementation. That way I can perhaps make comparisons with the other solutions
Okay, so I expected there to be topics on using Roc with Rust, but I didn't bother to look deeper to see if there were any bevy mentions in here, lol. Small world. Or bevy is bigger deal than I thought. In my defense, nothing came up when i looked up "bevy". I guess it was just looking at topic names.
That sounds like a perfect use case for a custom Roc platform.
Glad to hear it. So does Roc support running the code in a sandbox environment? What are the details on that?
Oh hold on. So if my host doesn't provide a way to change files or access the internet, Roc apps will simply not be able to do that. So my host can be the sandbox by design. Is that correct?
Yeah
Brendan Hansknecht said:
Yeah
I'm really not used to that. It makes too much sense lol.
I think I'll just give it a shot with making a host for Bevy and if I have any questions il post them here. But I'm already aware of some issues because in the past I actually did some research into integrating a runtime in Bevy for user-made script and I did not get very far :/ .
Actually i just realized there's #Writing a platform so I might post my questions there.
Gonna post here for now, #Writing a platform feels like a place for more intelligent conversation :rolling_on_the_floor_laughing:.
So I'm looking at a simple rust platform and it looks like a good place to start because its not super big but I'm not sure what the important files are and what anything means. There is very little documentation. If someone could go through it and put some comments in there, it would be a lot of help. If I missed something, please post a link. For now here is my interpretation of what I see:
main.roc looks important, it looks like a platform definition file. No clue whats going on there. A breakdown of what everything does would be nice.
The file that seems to be the bulk of the platform is lib.rs
. it has functions like roc_realloc and roc_dealloc. Are all of these roc_ functions expected to exist in every platform? And are the ones in lib.rs
gonna lead to memory leaks or any other memory management bugs? I was under the impression that the platform would need to do a bit more in terms of memory management, but it seems to me that Roc just needs a basic API to execute memory related tasks, such as allocating and deallocating, and Roc will just take it from there. I don't know if this is true though, but it is my current read of the situation. If that's true, then that's actually great news for me because I was anxious about how the memory management stuff was going to work and this seems very doable. Obviously for my case I still need to provide an API to deal with Bevy specific things, but my hope is that I wont have to write any unsafe blocks with memory management that I wont be able to effectively audit.
The last thing is rust_main. Im confused about what the implementation means in general for platform development. I imagined that the platform is just a Roc API with a non-Roc implementation. If a Roc app uses something from the Roc API, it actually executes code in the host language without needing to know about it. And when you create variables in the Roc App that require heap memory, im assuming roc_alloc gets called automatically without the user needing to know about it. Thats all cool, but rust_main seems to be iterating through the roc app and resolving "discriminants". But my expectation was that I wouldn't need to run code in a loop like this, and instead just provide a specific function signature, similar to how we roc_alloc is defined. So I guess my question here is: what role does rust_main play? Would I need to do something similar in my case?
Oh, we have a partially working effect interpreter example. Didn't realize that was in the repo
That is the future of how roc effects will work. It enables async execution of roc effects among other things.
That probably isn't a good example to base off of today because it is using trying to use effect interpreters (the discriminant and switch in rust main that you noted). I'm not sure the best starting point to look at though. Probably @Luke Boswell knows the best base for a rust platform with glue, maybe basic CLI, but it is pretty large now and would need a lot of trimming.
Brendan Hansknecht said:
... a rust platform with glue...
Im not super familiar with the concept of glue. I thought everything in this context would have it. But what you said implies it could not have it?
Glue is autogenerated host language (in this case rust) code to make interacting with roc easier.
Glue really only supports rust currently
Brendan Hansknecht said:
Glue is autogenerated host language (in this case rust) code to make interacting with roc easier.
Ah, so theoretically I could decide to not rely on this and make my own glue?
It also, has a number of missing features, but when it works, it avoids needing to know roc internals and deal with all of the segfaults of manually writing the code.
Brendan Hansknecht said:
Glue really only supports rust currently
Perfect, I'm a rust fanboy anyway :)
Yeah, the ffi can be written manually. That said, when glue is wrong or missing features, it is often best to manually edit it instead of starting from scratch.
Brendan Hansknecht said:
Yeah, the ffi can be written manually. And when glue is wrong or missing features, it is often best to manually edit it instead of starting from scratch.
Is the plan for Glue to eventually have more features and never be wrong?
Yeah. It should become the default everywhere
Brendan Hansknecht said:
Yeah. It should become the default everywhere
I really look forward to that. I find myself falling more in love with Roc as time passes :sweat_smile:
Yeah, I feel kinda bad for all the people working on platforms or interested rn. We are in a gap where a lot of things are planned (some in progress) that will be pretty big changes to exactly how platforms work. So currently documentation is low priority cause it will be stale very fast. Also, currently tooling is only ok, but still has gaps.
Hopefully medium term we will be transitioned with glue that just works and better docs, but there is a solid chunk of work to get there (just in platform impl changes before we even get to the glue or docs work)
Which so why currently, you kinda need someone like me to tell people the magic incantation to call roc successfully. That or just copy and paste and mess around.
Brendan Hansknecht said:
Yeah, I feel kinda bad for all the people working on platforms or interested rn. We are in a gap where a lot of things are planned (some in progress) that will be pretty big changes to exactly how platforms work. So currently documentation is low priority cause it will be stale very fast. Also, currently tooling is only ok, but still has gaps.
Hopefully medium term we will be transitioned with glue that just works and better docs, but there is a solid chunk of work to get there
I can't speak for everyone, but I really appreciate the work that you and other contributors do for cool projects like these, and I wouldn't want you to feel bad about something that is just a natural part of development. I am willing to wait longer if I need to, so no sweat!
I don't want to seriously touch this until Bevy releases their dynamic querying and system registration (which will happen in 3-9 months by my estimation). For now I just wanted to try to learn whatever I can out of curiosity.
Regarding platforms, I think basic-webserver is probably the most mature re glue. But there are some papercuts and workarounds to get it to work. Definitely not a reflection of the desired end state, but just workable enough to save writing a bunch of glue for basic types.
Also, are multiple different people working on a bevy related platform now/thinking about it?
Brendan Hansknecht said:
Also, are multiple different people working on a bevy related platform now/thinking about it?
Right now I'm just trying to assess the viability of Roc for Bevy, so just thinking about it I guess. Luke and Alexander are working on something and I plan to join the effort at some point once I have caught up with their work, but I don't know where that path will lead so I'm also thinking about how I would go about this myself.
Ah..makes sense
Okay so i looked at the basic-webserver. In my case, the data type id be sending into mainForHost is not knowable by the platform. Well, at least not during compile time. Let me explain...
I might try to implement the following design: each user-made script must provide at least 2 functions: run() and query(). The query function will return something representing the data to query in the Bevy world. So the platform would first run roc_app::query() and then use the result to query the world, and then pass that data into roc_app::run(), which could be anything, but it should be the same types found in the query. Is this something that would be doable? Roc is statically typed, but that should be fine on the Roc side because once the user creates the script and it compiles, it wont need to change again during runtime. But as the author of the platform, I wont be able to know what query() will return (on the rust side I bet we would be dealing with generics). This also means I wont know the data type to pass into run().
Has someone run into this kind of situation before?
But as the author of the platform, I wont be able to know what query() will return (on the rust side I bet we would be dealing with generics)
Using dynamic types over ffi definitely adds a layer of complication. In general, there are a handful of options here:
Box {}
. Aka a pointer to nothing. Obviously the pointer will be to real roc data. When the host needs to interact with the boxed data, it needs to a generic method in roc to do so. For example, it might store arbitrary roc data and then call an update and render method on that data in roc. Could also use a List {}
on the host side or similar if you want the data densely packed. But in general, this puts more responsibility on roc and likely more ffi communication and maybe more boxing.In general, I would lean towards 2 if you can make it work, if not probably 1 is best currently, but 3 would be really cool to see. But I would guess the cost isn't worth it especially for starting exploration.
Brendan Hansknecht said:
But as the author of the platform, I wont be able to know what query() will return (on the rust side I bet we would be dealing with generics)
Using dynamic types over ffi definitely adds a layer of complication. In general, there are a handful of options here:
- Probably the easiest answer is to use tagged types. So everything that is passed from roc into rust would be in a form that describes the data. Rust would then dynamically deal with this info at runtime. This is would would happen in the case of lua or python being embedded in a generic way. That said, it obviously has a runtime cost.
....
For option 1, could caching eliminate the runtime cost? I don't expect the types to change often. Actually, they should only change when the user changes them during some kind of dev session (imagining a hot reloading situation with script editing inside the game) but once they're in "game mode" i can guarantee that the types they ask for will never change.
I would have to think about it more and what exactly gets cached, but I think if you were to cache the type information, you might still have more complex/dynamic code, than fully static, but it should be a reasonable speed.
@peeps the whole point of thread about plugins in roc is to ultimately have working bevy example. However, currently there are some major stumbling blocks present. If you want I can add you into my repo where I make experiments with Roc-based bevy plugins with some help from roc dev @Luke Boswell . Code is currently here https://github.com/alexpyattaev/roc-plugin-example, let me know your github handle if you want to poke at it.
Alexander Pyattaev said:
peeps the whole point of thread about plugins in roc is to ultimately have working bevy example. However, currently there are some major stumbling blocks present. If you want I can add you into my repo where I make experiments with Roc-based bevy plugins with some help from roc dev Luke Boswell . Code is currently here https://github.com/alexpyattaev/roc-plugin-example, let me know your github handle if you want to poke at it.
Thanks for reaching out. I was going to clone the repo and mess around with it to figure out where you're at before reaching out with my thoughts/changes, but I'm having trouble finding time. But definitely in the near future. I also look forward to our meeting in January, I'm sure we will have some things to talk about :+1:
Luke Boswell said:
Regarding platforms, I think basic-webserver is probably the most mature re glue. But there are some papercuts and workarounds to get it to work. Definitely not a reflection of the desired end state, but just workable enough to save writing a bunch of glue for basic types.
Can you help me get this to work? Currently getting the error in the screenshot (sorry, copy/pasting was not working so had to screenshot).
image.png
I wont be able to investigate this today, just out with family. I should be able to look at it tomorrow sometime. Can you run glue manually without the script?
Actually it's probably because that script assumes roc repository is at a particlar place relative. Do you have roc cloned on your system? I am not sure if it works just using nightlies
Luke Boswell said:
I wont be able to investigate this today, just out with family. I should be able to look at it tomorrow sometime. Can you run glue manually without the script?
No worries! No rush at all. Happy holidays :evergreen_tree:
Luke Boswell said:
Actually it's probably because that script assumes roc repository is at a particlar place relative. Do you have roc cloned on your system? I am not sure if it works just using nightlies
I have it cloned on my system cus I needed the crates folder (was getting a different error before). But its using the nightly build for the actual roc command.
Oh thats weird, i think its working manually
i assumed one of the roc glue commands was throwing the error but i checked both and they work when i manually run them.
Okay so I didn't know I had to make helloweb.roc myself (its not in the repo anywhere). But i just did and its working. Cool. I still want that bash script to work tho so il play around with it to see whats up.
Maybe we made some changes and didnt update that script. Its not exactly checked in CI or anything. Also I cant remember but the glue that is generated may also have some minor tweaks by hand. I think we had an issue with generating invalid assert statements that can just be commented out.
I see. Thanks for the help :+1:
So I was trying to get the glue example to work and I was getting errors when trying to run the app so i just listened to the error and made the changes to the generated glue. Surprisingly, it actually allowed the app to run! Unfortunately, I got this runtime error:
🔨 Rebuilding platform...
Let's do things!
free(): invalid pointer
Aborted
This is a sample of one of the errors i was getting prior to this (which was fixed by adding a Clone derive in 2 places):
Rebuilding platform...
An internal compiler expectation was broken.
This is definitely a compiler bug.
Please file an issue here: https://github.com/roc-lang/roc/issues/new/choose
thread '<unnamed>' panicked at 'Error:
Failed to rebuild src/main.rs:
The executed command was:
cargo build --bin host
stderr of that command:
Compiling roc_app v1.0.0 (/home/peepo/stuff/roc/projects/roc/examples/glue/rust-platform/roc_app)
error[E0277]: the trait bound `RocFunction_67: Clone` is not satisfied
--> roc_app/src/x86_64.rs:75:5
|
71 | }#[derive(Clone, Debug, )]
| ----- in this derive macro expansion
...
75 | pub f1: RocFunction_67,
| ^^^^^^^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `RocFunction_67`
|
= note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider annotating `RocFunction_67` with `#[derive(Clone)]`
|
53 + #[derive(Clone)]
54 | pub struct RocFunction_67 {
|
For more information about this error, try `rustc --explain E0277`.
error: could not compile `roc_app` (lib) due to previous error
', crates/compiler/build/src/link.rs:1414:21
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
thread 'main' panicked at 'Failed to (re)build platform.: Any { .. }', crates/compiler/build/src/program.rs:976:46
Roc version output: roc nightly pre-release, built from commit 613c6c4 on Di 26 Dez 2023 09:01:44 UTC
It is invalid to just clone a roc function
So you probably need to remove instead of add more clone
Brendan Hansknecht said:
So you probably need to remove instead of add more clone
Next i get this:
An internal compiler expectation was broken.
This is definitely a compiler bug.
Please file an issue here: https://github.com/roc-lang/roc/issues/new/choose
thread '<unnamed>' panicked at 'Error:
Failed to rebuild src/main.rs:
The executed command was:
cargo build --bin host
stderr of that command:
Compiling roc_app v1.0.0 (/home/peepo/stuff/roc/projects/roc/examples/glue/rust-platform/roc_app)
error[E0599]: the method `clone` exists for struct `ManuallyDrop<Op_StderrWrite>`, but its trait bounds were not satisfied
--> roc_app/src/x86_64.rs:255:69
|
44 | pub struct Op_StderrWrite {
| ------------------------- doesn't satisfy `Op_StderrWrite: Clone`
...
255 | StderrWrite: unsafe { payload_union.StderrWrite.clone() },
| ^^^^^ method cannot be called on `ManuallyDrop<Op_StderrWrite>` due to unsatisfied trait bounds
|
::: /home/peepo/.rustup/toolchains/1.71.1-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/mem/manually_drop.rs:50:1
|
50 | pub struct ManuallyDrop<T: ?Sized> {
| ---------------------------------- doesn't satisfy `ManuallyDrop<Op_StderrWrite>: Clone`
|
= note: the following trait bounds were not satisfied:
`Op_StderrWrite: Clone`
which is required by `ManuallyDrop<Op_StderrWrite>: Clone`
= help: items from traits can only be used if the trait is implemented and in scope
= note: the following trait defines an item `clone`, perhaps you need to implement it:
candidate #1: `Clone`
help: consider annotating `Op_StderrWrite` with `#[derive(Clone)]`
|
44 + #[derive(Clone)]
45 | pub struct Op_StderrWrite {
|
Similar for StdoutWrite.
Does StdoutWrite take a closure? If so, it also can't have clone (need to follow all the way up the chain). That said, not sure why it would take a closure currently. Should just be an FX function that takes a string and returns nothing.
Oh, that says op_StdoutWrite, so is probably based off of some of the earlier testing for effect interpreter stuff and not sure the current effect infrastructure?
Brendan Hansknecht said:
Oh, that says op_StdoutWrite, so is probably based off of some of the earlier testing for effect interpreter stuff and not sure the current effect infrastructure?
Im sorry but idk enough to answer these questions but like, i can share the file with you i think.
x86_64.rs
Brendan Hansknecht said:
Oh, that says op_StdoutWrite, so is probably based off of some of the earlier testing for effect interpreter stuff and not sure the current effect infrastructure?
This is under examples/glue from the roc repo, if that answers your question.
Ah yeah. That is early testing for the effect interpreter changes to come. So it isn't really a standard platform. We probably should put a more standard platform in the examples and move that to a testing location.
Brendan Hansknecht said:
Ah yeah. That is early testing for the effect interpreter changes to come. So it isn't really a standard platform. We probably should put a more standard platform in the examples and move that to a testing location.
Oh alright. Part of the reason why I wanted to try was because it was upcoming changes. I didnt want to learn anything that I was going to throw out anyway. At the moment I'm feeling pretty dissuaded, like maybe I should wait a bit before tackling Roc again.
Yeah, platforms are in a state where they are possible to make, but definitely hobbled together/require specific knowledge to make correctly. So it requires more effort to figure out the nuances.
Last updated: Jul 06 2025 at 12:14 UTC