-
-
Notifications
You must be signed in to change notification settings - Fork 57
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Deregister exeternal value #171
Comments
Thanks for opening this. There is an API that exists for passing scoped references in to the engine that removes the value afterwards, however I don't think there is anything for value types at the moment that matches that API. Could you provide an example of the API you're looking for? Here is an example using the scoped reference API that already exists (forgive the scratch, I'm away from my computer so can't get the exact example yet): /// somewhere there is something like "impl custom reference" for Context
engine.with_mut_reference::<Context, Context>(cx).consume(
move |engine, arguments| {
let context = arguments[0].clone();
engine.update_value("*helix.cx*", context);
engine.call_function_by_name_with_args(name, args.clone())
},
) |
Thanks, this API looks helpful and is generally the pattern I want. Unfortunately, I'm working with external types which complicates things a bit. Right now, the proc macro for |
I agree with what you've said - let me try to outline how I think you could use the existing API to suit your needs, and I'll also investigate what it would take to do otherwise: struct Context<'a>(&'a TypeNameHere);
impl<'a> CustomReference for Context<'a> {}
steel::custom_reference!(Context<'a>);
fn test(cx: &mut Context) {
engine.with_mut_reference::<Context, Context>(cx).consume(
move |engine, arguments| {
let context = arguments[0].clone();
// Update value so the same variable is used
engine.update_value("*helix.cx*", context);
engine.call_function_by_name_with_args(name, args.clone())
},
)
} |
A few issues I'm running into. Here's my code: #[derive(Debug, Steel, Clone)]
struct WorldHolder<'w>(UnsafeWorldCell<'w>);
impl <'w> CustomReference for WorldHolder<'w> {}
steel::custom_reference!(WorldHolder<'a>); 1: error[E0726]: implicit elided lifetime not allowed here
--> src/script/mod.rs:33:8
|
33 | struct WorldHolder<'w>(UnsafeWorldCell<'w>);
| ^^^^^^^^^^^ expected lifetime parameter
|
help: indicate the anonymous lifetime
|
33 | struct WorldHolder<'_><'w>(UnsafeWorldCell<'w>); Not sure why the proc maco is causing the compiler to output this invalid 2: Much more minor ergonomic issue, I'm also not totally in love with the API. The purepose of the dual type params isn't totally clear from the call site, and it seems like the user will almost always want to call engine.with_mut_reference("foo", foo, |engine| {
// do whatever
}); where the call to create the scope would register the variable itself to reduce some of the boilerplate. Btw, really impressed with this crate and looking forward to using it more. I've been dying for an embedded lisp in Rust and was really encouraged to see this project despite whatever rough edges it may still have. |
Thank you for the kind words, I appreciate it 😄 You're right, the dual type params are confusing and I don't like it either. I haven't yet figured out how to only specify the one, and this point it will just require some more thinking on the API and trying some things out to make the type checker happy. You're also right on the macro, it does need the You're also spot on with the API - that API is used when passing multiple references, there is a happy path that exists already that does exactly what you want I believe: // This a method on `Engine`
pub fn run_thunk_with_reference<
'a,
'b: 'a,
T: CustomReference + 'b,
EXT: CustomReference + 'static,
>(
&'a mut self,
obj: &'a mut T,
mut thunk: impl FnMut(&mut Engine, SteelVal) -> Result<SteelVal>,
) -> Result<SteelVal>
where
T: ReferenceMarker<'b, Static = EXT>,
{
self.with_mut_reference(obj).consume(|engine, args| {
let mut args = args.into_iter();
thunk(engine, args.into_iter().next().unwrap())
})
} Per the issue you're having - can you try removing the |
Unfortunately, when I remove There might be a bit of an x/y problem here in terms of what I'm describing. In most other scripting engines I've used, functions on the Rust side are defined something like This kind of signature / pattern is much less ergonomic for writing Rust glue, since it requires pulling values out of an untyped arguments object, checking for arity, etc. I honestly really like how convenient it is to write Steel functions in Rust with POD that "just work." I'm also not totally bothered by passing my context object as the first argument to everything, since this is a very "lispy" kind of pattern anyway. But yeah, that's a bit more color and elaboration on what I'm trying to accomplish. :) Edit: I should also say the current examples use a once_cell mutex approach to this problem, which is totally cool and works! This is more about ergonomics. |
So I might be missing something, however I'm not sure why you're no longer able to use the value as the input to your functions. Could you be more specific about how it is not working? It should still work as defined, where the first argument can be an https://github.com/mattwparas/helix/blob/steel-event-system/helix-term/src/commands/engine/steel.rs For example, this function accepts a context and can just be registered into a module no problem: It might be just an oversight in the API as it is now that its not working for you right now. Edit: The |
Thanks, this is a good reference. I like your solution for templating functions that hide passing your context object. 👍 Here's the error I'm getting when I remove the error[E0277]: the trait bound `Engine: RegisterFn<for<'a, 'b> fn(&'a WorldHolder<'b>, std::string::String) -> std::option::Option<script::EntityRef> {op}, _, _>` is not satisfied
--> src/script/mod.rs:72:40
|
72 | .register_fn("op", op)
| ----------- ^^ the trait `RegisterFn<for<'a, 'b> fn(&'a WorldHolder<'b>, std::string::String) -> std::option::Option<script::EntityRef> {op}, _, _>` is not implemented for `Engine`
| |
| required by a bound introduced by this call
|
= help: the following other types implement trait `RegisterFn<FN, ARGS, RET>`:
<Engine as RegisterFn<FN, steel::steel_vm::register_fn::Wrapper<()>, RET>>
<Engine as RegisterFn<FN, steel::steel_vm::register_fn::Wrapper<(A,)>, RET>>
<Engine as RegisterFn<FN, steel::steel_vm::register_fn::Wrapper<(A, B)>, RET>>
<Engine as RegisterFn<FN, AsyncWrapper<()>, RET>>
<Engine as RegisterFn<FN, AsyncWrapper<(A,)>, RET>>
<Engine as RegisterFn<FN, MarkerWrapper1<(A, B)>, RET>>
<Engine as RegisterFn<FN, MarkerWrapper1<SELF>, RET>>
<Engine as RegisterFn<FN, MarkerWrapper2<(A, B)>, RET>>
and 41 others Here's the relevant code: #[derive(Debug, Clone)]
struct WorldHolder<'w>(UnsafeWorldCell<'w>);
impl <'w> CustomReference for WorldHolder<'w> {}
steel::custom_reference!(WorldHolder<'a>);
#[derive(Debug, Steel, PartialEq, Clone)]
pub struct EntityRef(Entity);
fn op(world: &WorldHolder, name: String) -> Option<EntityRef> {
None
} |
aha, this is almost assuredly just a short coming with the register function api (which I'm happy to fix) - can you try making that an |
Ah! Great. Everything is working now. Yeah, in practice it's fine for these to have exclusive access, but I'd like to be able to enforce read only access for getters vs setters, etc. I'm going to read through your Helix implementation some more, but this is working for now. :) |
Awesome - yeah so this whole api was certainly gnarly to implement and as you can probably tell, I implemented it so that I could get the helix API to work nicely (and more or less, stopped there). There are almost assuredly things I've missed and nuances that I didn't cover. Feel free to open issues / continue here / do whatever with use cases if something doesn't work, happy to add more to this Realistically you'll probably try to add a function soon and you think it'll work and it will just say "absolutely not" with some horribly complex compiler message. If that occurs, the fastest way I can fix it is to just hit me with the function signature you're trying to add and I'll see why it doesn't work (and most likely, add the patch quickly that will make it work) 😄 |
There are some (unsafe) values that I need to insert into the engine that have the lifetime of a single script execution. In order to maintain my invariants, I would like to be able to deregister an external value, so that I can remove it from the engine after my script completes.
The text was updated successfully, but these errors were encountered: