Stream: beginners

Topic: Roc as an embedded scripting language for a Bevy game


view this post on Zulip peeps (Dec 20 2023 at 23:54):

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

view this post on Zulip peeps (Dec 21 2023 at 01:44):

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.

view this post on Zulip Sky Rose (Dec 21 2023 at 23:34):

That sounds like a perfect use case for a custom Roc platform.

view this post on Zulip peeps (Dec 22 2023 at 00:33):

Glad to hear it. So does Roc support running the code in a sandbox environment? What are the details on that?

view this post on Zulip peeps (Dec 22 2023 at 00:45):

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?

view this post on Zulip Brendan Hansknecht (Dec 22 2023 at 00:55):

Yeah

view this post on Zulip peeps (Dec 22 2023 at 01:40):

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 :/ .

view this post on Zulip peeps (Dec 22 2023 at 02:02):

Actually i just realized there's #Writing a platform so I might post my questions there.

view this post on Zulip peeps (Dec 22 2023 at 22:01):

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?

view this post on Zulip Brendan Hansknecht (Dec 22 2023 at 22:35):

Oh, we have a partially working effect interpreter example. Didn't realize that was in the repo

view this post on Zulip Brendan Hansknecht (Dec 22 2023 at 22:36):

That is the future of how roc effects will work. It enables async execution of roc effects among other things.

view this post on Zulip Brendan Hansknecht (Dec 22 2023 at 22:38):

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.

view this post on Zulip peeps (Dec 22 2023 at 22:40):

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?

view this post on Zulip Brendan Hansknecht (Dec 22 2023 at 22:41):

Glue is autogenerated host language (in this case rust) code to make interacting with roc easier.

view this post on Zulip Brendan Hansknecht (Dec 22 2023 at 22:42):

Glue really only supports rust currently

view this post on Zulip peeps (Dec 22 2023 at 22:42):

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?

view this post on Zulip Brendan Hansknecht (Dec 22 2023 at 22:42):

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.

view this post on Zulip peeps (Dec 22 2023 at 22:43):

Brendan Hansknecht said:

Glue really only supports rust currently

Perfect, I'm a rust fanboy anyway :)

view this post on Zulip Brendan Hansknecht (Dec 22 2023 at 22:43):

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.

view this post on Zulip peeps (Dec 22 2023 at 22:44):

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?

view this post on Zulip Brendan Hansknecht (Dec 22 2023 at 22:44):

Yeah. It should become the default everywhere

view this post on Zulip peeps (Dec 22 2023 at 22:46):

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:

view this post on Zulip Brendan Hansknecht (Dec 22 2023 at 22:51):

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)

view this post on Zulip Brendan Hansknecht (Dec 22 2023 at 22:51):

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.

view this post on Zulip peeps (Dec 22 2023 at 22:56):

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.

view this post on Zulip Luke Boswell (Dec 22 2023 at 22:58):

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.

view this post on Zulip Brendan Hansknecht (Dec 22 2023 at 23:01):

Also, are multiple different people working on a bevy related platform now/thinking about it?

view this post on Zulip peeps (Dec 22 2023 at 23:08):

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.

view this post on Zulip Brendan Hansknecht (Dec 22 2023 at 23:21):

Ah..makes sense

view this post on Zulip peeps (Dec 23 2023 at 03:44):

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?

view this post on Zulip Brendan Hansknecht (Dec 23 2023 at 16:06):

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:

  1. 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.
  2. This next option is very use case specific as to whether or not it can be applied. First, you make all the data that roc is passing back to the host opaque. The host will receive what it thinks is a 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.
  3. Last option would be the most complicated, but it would be to always compile the platform for the specific instance of the application. You would write the the platform using generics in rust. When compiling the roc app, each compilation would modify the platform and regenerate glue or have some other for of autogenerated rust code to deal with the generics. This has the potentially to be highest performance because it keeps everything static always. That said, it is much more complicated to manage and always requires the platform on an application data type changes.

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.

view this post on Zulip peeps (Dec 23 2023 at 16:18):

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:

  1. 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.

view this post on Zulip Brendan Hansknecht (Dec 23 2023 at 16:34):

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.

view this post on Zulip Alexander Pyattaev (Dec 23 2023 at 21:04):

@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.

view this post on Zulip peeps (Dec 23 2023 at 21:22):

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:

view this post on Zulip peeps (Dec 25 2023 at 22:45):

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

view this post on Zulip Luke Boswell (Dec 25 2023 at 23:29):

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?

view this post on Zulip Luke Boswell (Dec 25 2023 at 23:33):

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

view this post on Zulip peeps (Dec 25 2023 at 23:34):

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.

view this post on Zulip peeps (Dec 25 2023 at 23:36):

Oh thats weird, i think its working manually

view this post on Zulip peeps (Dec 25 2023 at 23:39):

i assumed one of the roc glue commands was throwing the error but i checked both and they work when i manually run them.

view this post on Zulip peeps (Dec 25 2023 at 23:42):

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.

view this post on Zulip Luke Boswell (Dec 26 2023 at 00:30):

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.

view this post on Zulip peeps (Dec 26 2023 at 00:40):

I see. Thanks for the help :+1:

view this post on Zulip peeps (Dec 27 2023 at 02:07):

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

view this post on Zulip Brendan Hansknecht (Dec 27 2023 at 02:20):

It is invalid to just clone a roc function

view this post on Zulip Brendan Hansknecht (Dec 27 2023 at 02:21):

So you probably need to remove instead of add more clone

view this post on Zulip peeps (Dec 27 2023 at 02:27):

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.

view this post on Zulip Brendan Hansknecht (Dec 27 2023 at 04:11):

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.

view this post on Zulip Brendan Hansknecht (Dec 27 2023 at 04:15):

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?

view this post on Zulip peeps (Dec 27 2023 at 04:21):

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

view this post on Zulip peeps (Dec 27 2023 at 04:24):

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.

view this post on Zulip Brendan Hansknecht (Dec 27 2023 at 04:34):

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.

view this post on Zulip peeps (Dec 27 2023 at 04:40):

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.

view this post on Zulip Brendan Hansknecht (Dec 27 2023 at 16:48):

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