-
-
Notifications
You must be signed in to change notification settings - Fork 401
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
Add a ContextData struct to inject host defined types from the context #3802
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #3802 +/- ##
==========================================
+ Coverage 47.24% 50.27% +3.03%
==========================================
Files 476 459 -17
Lines 46892 44968 -1924
==========================================
+ Hits 22154 22608 +454
+ Misses 24738 22360 -2378 ☔ View full report in Codecov by Sentry. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a lot more on the fence about this change.
I think the behaviour of the new traits is very obvious at first glance: convert the original JsValue
arguments into the provided types, in positional order. However, adding this API would add an arbitrary conversion that doesn't really map as cleanly to the current semantics, since there are a LOT of places from where a HostDefined
could come from: the current Script
, the current Module
, the current Realm
or (in the near future) the current Context
; all of them accessible from the passed context.
That being said, what problem does this new API try to solve? I think we could find an alternative that has more "predictable" semantics than the current proposal.
I have state that I need to pass down to the JS API. This allows me to pass the state around without having to mess with the context, which has a surprising amount of boilerplate. It also reduces API friction and semantic mapping; you don't need to worry too much about Context or Realm or other objects from Boa. You can focus on your own code. This is what my code looks like: https://github.com/hansl/golem/blob/golem-script/src/golem-script-host/src/modules/golem/db.rs. Check the I'm willing to listen to alternatives, but it's common for dependency injection to not worry too much about order of arguments. People will order them as they see fit; whether it's app-specific arguments first, then positional arguments, then environmental context, or whatever order. I'm not sure I see the confusion. I really think this improves coding in complex applications. See Rocket/Axum for good examples of dependency injection where the order of arguments is very loose. |
I'm not really worried about the order of arguments per se. What I'm really worried about is the I'm aware that the docs could explain where to set the values for
Maybe this can be solved in a clearer way by having the aforementioned let f = (|HostDefined(host): HostDefined<CustomHostDefinedStruct>| {
host.counter + 1
}).into_js_function_copied(&mut context); is barely equal in length to: let f = (|context: Context| {
let counter = context.host_defined_mut().try_get::<CustomHostDefinedStruct>()?;
*counter += 1
}).into_js_function_copied(&mut context); But on the second version you can tell at first glance from where the host defined data is fetched, and it also avoids cloning the data! EDIT: We could even make the host defined data first-class, and have special functions to just pull the data directly from the context: let f = (|context: Context| {
let counter = context.try_get::<CustomHostDefinedStruct>()?;
*counter += 1
}).into_js_function_copied(&mut context); |
There is another advantage to my approach here of using injection like those to pass data; the error handling of transformation/reaching for that data is moved outside of the function itself. Ironically, only your first example would compile (the one that uses the HostDefined argument), because the compiler wouldn't be able to infer the full return type of your other examples (remember that let f = (|context: Context| -> JsResult<()> {
let counter = context.try_get::<CustomHostDefinedStruct>()?;
*counter += 1;
Ok(())
}).into_js_function_copied(&mut context); My code is significantly simpler at this point. And the user doesn't have to worry about return types. let f = (|HostDefined(counter): HostDefined<Counter>| counter + 1).into_js_function_copied(&mut context); The biggest advantage of yours is the mutability reference of the original, instead of cloning. I couldn't get the reference to work for this since the Context lifetime is weird. I can try again tomorrow. |
Naming things is hard. |
I'm also not really worried about the name itself. I'm mostly worried about the concept of having a host defined argument that isn't really clear where it comes from without reading the implementation of |
That's simply because I decided to show the mutability of the proposed API. If we want to compare it with your API in a fair way, it would be something like: let f = (|context: &mut Context| {
context.try_get::<CustomHostDefinedStruct>().map(|h| h.counter + 1)
}).into_js_function_copied(&mut context); Which is still a bit longer, but without hidden assumptions about where the data comes from. |
Following offline discussion, there can be multiple Changed the PR to add one, and changed this new injector to reflect that the Data comes from Context. We might extend to other semantics in other injectors later (e.g. having multiple realm merged into one, etc). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pretty satisfied with this new API. Thank you for the good work!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great! Just needs a rebase :)
@HalidOdat Done. PTAL. |
No description provided.