-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
SystemRunner param and macro syntax #21811
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
base: main
Are you sure you want to change the base?
Conversation
bbc70c7 to
e0c905c
Compare
chescock
left a comment
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.
This is really cool!
I think this will close #16680, so you might want to link that in the PR description.
I left a lot of comments, but they're mostly style nitpicks and brainstorming for future possibilities. The only thing that I think really needs attention is the map_err(RunSystemError::Skipped) part, since that will silently ignore missing resources. And the CI failure :).
| pub fn system<'w, 's, In, Out, Marker, Sys>( | ||
| system: Sys, | ||
| ) -> impl SystemParamBuilder<SystemRunner<'w, 's, In, Out, Sys::System>> |
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.
Do the lifetimes here matter, or can they all be 'static?
| pub fn system<'w, 's, In, Out, Marker, Sys>( | |
| system: Sys, | |
| ) -> impl SystemParamBuilder<SystemRunner<'w, 's, In, Out, Sys::System>> | |
| pub fn system<In, Out, Marker, Sys>( | |
| system: Sys, | |
| ) -> impl SystemParamBuilder<SystemRunner<'static, 'static, In, Out, Sys::System>> |
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.
was just following the other methods tbh, I can make those static though 👍
| Uninitialized { | ||
| builder: Builder, | ||
| func: Func, | ||
| meta: SystemMeta, |
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.
Is this here only to support with_name? (It's also used in get_last_run/set_last_run, but I think those are only supposed to be valid on initialized systems.) It might be better to just store a DebugName. If the builder is large, this could wind up being the largest variant and taking up space even after the system is built.
| /// ) | ||
| /// .build_state(&mut world) | ||
| /// .build_system( | ||
| /// |mut run_a: SystemRunner<(), u32>, mut run_b: SystemRunner<(), u32>| -> Result<u32, RunSystemError> { |
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 think the CI failure is because this is using the default of dyn System instead of inferring the FunctionSystem types for count_a and count_b.
| /// |mut run_a: SystemRunner<(), u32>, mut run_b: SystemRunner<(), u32>| -> Result<u32, RunSystemError> { | |
| /// |mut run_a: SystemRunner<(), u32, _>, mut run_b: SystemRunner<(), u32, _>| -> Result<usize, RunSystemError> { |
Alternately, you might want a builder method for dyn System, like
pub fn dyn_system<In, Out, Marker, Sys>(
system: Sys,
) -> impl SystemParamBuilder<SystemRunner<'static, 'static, In, Out, dyn System<In = In, Out = Out>>>
where
In: SystemInput + 'static,
Out: 'static,
Sys: IntoSystem<In, Out, Marker>,
{
SystemRunnerBuilder::boxed(Box::new(IntoSystem::into_system(system)))
}| /// ParamBuilder::system(count_b) | ||
| /// ) | ||
| /// .build_state(&mut world) | ||
| /// .build_system( |
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.
The other CI failure is because it can't tell whether the return type is u32 or Result<u32>. You can supply it like
| /// .build_system( | |
| /// .build_system::<_, u32, _, _>( |
But it might look nicer to run the system and annotate the result, like
/// let result: usize = world.run_system_once(get_sum).unwrap();
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.
how does add_systems infer which to use?
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.
how does
add_systemsinfer which to use?
It just always requires Out = () for systems and Out = bool for conditions.
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.
Oh yeah true. And the other combinators can set it explicitly, sounds good.
| } | ||
|
|
||
| /// Create a [`System`] from a [`SystemParamBuilder`] | ||
| fn build_system<Marker, Func, Out>(self, func: Func) -> BuilderSystem<Marker, Self, Func, Out> |
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 noticed that compose! systems aren't usable with run_system_cached because they don't have a ZST IntoSystem. That never seemed important for system builders in the past, because they generally used some dynamic data and would never be a ZST, but compose! systems won't always capture state.
If we want to allow them to be used with run_system_cached, I think there are two pieces:
- We need an
IntoBuilderSystem. It would holdbuilderandfuncbut notmeta, so it could be a ZST if the builder is. - We need
SystemRunnerBuilderto store animpl IntoSysteminstead of aBox<impl System>, and then callinto_systeminside ofbuild. That makes theBoxedSystemcase a little more awkward, but I think it's still possible.
| /// ```ignore | ||
| /// let system_a = |world: &mut World| { 10 }; | ||
| /// let system_b = |a: In<u32>, world: &mut World| { println!("{}", *a + 12) }; | ||
| /// compose! { |
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.
Would it make sense to accept a function definition instead of or in addition to a closure? That would make it easier for these systems to have names. Something like
compose! {
fn system_name() -> Result<(), RunSystemError> {
run!(system_a)
}
}For that matter, would it make sense to use an attribute macro, like
#[compose]
fn system_name() -> Result<(), RunSystemError> {
run!(system_a)
}?
... Oh, maybe not, because what you'd really want that to expand to is const system_name: impl System = const { ... };, but there's no way to write the type for the const.
I think I got As you mentioned, the big problem is that the resulting systems take pub trait IntoSystem<In: SystemInput, Out, Marker>: Sized {
// ...
fn pipe2<B, BIn, BOut, MarkerB>(
self,
system: B,
) -> impl System<In = StaticSystemInput<'static, In>, Out = BOut>
where
Out: 'static,
B: IntoSystem<BIn, BOut, MarkerB> + 'static,
for<'a> BIn: SystemInput<Inner<'a> = Out> + 'static,
In: 'static,
BOut: 'static,
Marker: 'static,
MarkerB: 'static,
Self: 'static,
{
compose_with!(
|StaticSystemInput(input): StaticSystemInput<In>| -> Result<BOut, RunSystemError> {
let value = run!(self, input)?;
run!(system, value)
}
)
}pub trait SystemCondition<Marker, In: SystemInput = ()>:
// ...
fn and2<M: 'static, C: SystemCondition<M, In> + 'static>(
self,
and: C,
) -> impl ReadOnlySystem<In = StaticSystemInput<'static, In>, Out = bool>
where
for<'a> In: SystemInput<Inner<'a>: Copy> + 'static,
Self: 'static,
Marker: 'static,
{
compose_with!(
|StaticSystemInput(input): StaticSystemInput<In>| -> Result<bool, RunSystemError> {
Ok(run!(self, input)? && run!(and, input)?)
}
)
} |
|
Thanks for the input! I'll spin up a few PRs for Also I realized |
Objective
resolves #16680
Add a new system param for running systems inside other systems. Also, I've included some macros for nice syntax on top.
I'm pretty proud of how nice I was able to make this, but there's still a bit of work to do, especially around generic code :)
Testing
pipeandmapimpls, but failed. This PR is pretty robust for most use cases but isn't fully ready for generic code yet. In particular, it's difficult to make sure the input types match (often have to wrap withStaticSystemInput) andReadOnlySystembound is inferred correctly when using for run conditions. These only really matter for combinators likepipeandmapthough, since otherwise they're run exactly the same.Showcase
Click to view showcase