Skip to content
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

Remove the ability to use strings as labels #4409

Closed

Conversation

alice-i-cecile
Copy link
Member

@alice-i-cecile alice-i-cecile commented Apr 4, 2022

Objective

Solution

  • Remove the ability for string-like objects to be used as labels.
  • Yeet!

Changelog

  • In order to promote more reliable, higher quality code, string-like types can no longer be used as labels for AppLabel, StageLabel, SystemLabel, AmbiguitySetLabel or RunCriteriaLabel.

Migration Guide

String-like types can no longer be used as labels for AppLabel, StageLabel, SystemLabel, AmbiguitySetLabelorRunCriteriaLabel. Use a constructed type (including a type that stores data) instead. In the most common case of a SystemLabel, you should strongly consider using an implicit SystemTypeIdLabel` instead.

For example, suppose your initial code was:

app
  .add_system(gravity.label("gravity"))
  .add_system(forces.after("gravity");

This could either become:

app
  .add_system(gravity)
  .add_system(forces.after(gravity);

Or:

#[derive(SystemLabel, Debug, PartialEq, Eq, Clone, Hash)]
enum PhysicsLabels {
  Gravity,
  // Generally, related labels are grouped together in an enum
  // to provide helpful structure to the end consumer
  ...
}

app
  .add_system(gravity.label(PhysicsLabels::Gravity))
  .add_system(forces.after(PhysicsLabels::Gravity);

Prefer implicit system labels over explicit ones (for clarity and brevity) whenever they do not need to be exposed to an external consumer. By contrast, explicit system labels can be useful to expose a thoughtful and stable external API that consumers can rely upon without excessive breakage when you modify the internals of your plugin.

Status

@github-actions github-actions bot added the S-Needs-Triage This issue needs to be labelled label Apr 4, 2022
@alice-i-cecile alice-i-cecile marked this pull request as draft April 4, 2022 05:41
@alice-i-cecile alice-i-cecile added A-ECS Entities, components, systems, and events C-Usability A simple quality-of-life change that makes Bevy easier to use C-Breaking-Change A breaking change to Bevy's public API that needs to be noted in a migration guide and removed S-Needs-Triage This issue needs to be labelled labels Apr 4, 2022
@hymm
Copy link
Contributor

hymm commented Apr 5, 2022

I think I'm onboard with this change. Can you flesh out the example in the migration guide section, so that we can see exactly what the extra boilerplate over using strings would entail?

@alice-i-cecile
Copy link
Member Author

alice-i-cecile commented Apr 5, 2022

@hymm done :) In most cases the boilerplate change is negative: explicit labels should be reserved for when you're creating an API for others to hook into a plugin (in Bevy, the ecosystem or in a large project).

@alice-i-cecile alice-i-cecile added the X-Controversial There is active debate or serious implications around merging this PR label Apr 22, 2022
@alice-i-cecile alice-i-cecile added this to the Bevy 0.8 milestone Apr 25, 2022
@inodentry
Copy link
Contributor

inodentry commented Apr 26, 2022

Firstly, I don't like the argument that the existence of stringly labels encourages bad code, and that users need to be forced to use typed labels. I think everyone is well aware of the downsides and upsides.

Now, I am personally firmly against removing stringly labels for the time being, at least until we at least land a few other important changes.

So, there are currently 4 things (afaik) where labels are used:

  • Systems
  • Stages
  • RunCriteria
  • Ambiguity Sets

All your current discussion so far seems to be focused on systems. I understand the argument that we now have an even more convenient way of ordering systems, using the function. No complaints here.

Run Criteria are going to be completely redesigned with Stageless (and will no longer need labels), and in their current state are largely unusable in practice, so nothing is lost here.

Ambiguity Sets I cannot comment on, because I have never personally used any Bevy APIs related to ambiguities.

Stages, however, are still a necessary reality of life. Until Stageless is done, they are the only way to accomplish a lot of practical use cases. Personally, this is still my major use case for stringly labels, as it can be a big productivity booster. Saves a lot of time while prototyping, and greatly improved my game jam experience. :)

I propose we do not remove stringly labels until after Stageless.

@inodentry
Copy link
Contributor

inodentry commented Apr 26, 2022

To counter arguments like "it's just a little more typing!" and "you already don't mind derive component", I responded to that in Discord, and I will quote it here:

i guess what really annoys me isn't merely that boilerplate requires more typing ... it is the cognitive overhead and also jumping around the codebase

  • if i am creating a new component type, i have to define and name a new type anyway, and i know im going to be using it as a component ... so ...

just one #[derive(Component)] is fine, i don't have to go out of my way to think about it, and also it goes right where my cursor already is, where im already typing (i'm creating a struct, it goes on the struct)

  • if i want a quick and dirty label for something ...
  1. now, instead of just a name for the label, i also have to think about creating and naming a type, and remember what the exact long list of derives it required was ...

  2. and i have to jump to a different place in my text editor to define the type, because now i have to do something in 2 different places ... so i have to move away from wherever im currently typing (in the app builder stuff) ... and copypasting the derives is not an improvement ... it makes this even worse (have to go find a place to copy them from)

  • derive component takes like 2 more seconds of typing
  • typed labels take more like 15+ seconds of performing multiple text editor actions, and also having to think about unrelated distracting stuff (like what derives it needs)

it's annoying

To improve the user experience, we should focus on addressing these usability issues with typed labels, first.

If typed labels (and state types for that matter) didn't require a long magic list of derives, that would be a pretty big usability improvement.

@alice-i-cecile alice-i-cecile removed this from the Bevy 0.8 milestone Jun 26, 2022
bors bot pushed a commit that referenced this pull request Jul 14, 2022
# Objective

- Closes #4954 
- Reduce the complexity of the `{System, App, *}Label` APIs.

## Solution

For the sake of brevity I will only refer to `SystemLabel`, but everything applies to all of the other label types as well.

- Add `SystemLabelId`, a lightweight, `copy` struct.
- Convert custom types into `SystemLabelId` using the trait `SystemLabel`.

## Changelog

- String literals implement `SystemLabel` for now, but this should be changed with #4409 .

## Migration Guide

- Any previous use of `Box<dyn SystemLabel>` should be replaced with `SystemLabelId`.
- `AsSystemLabel` trait has been modified.
    - No more output generics.
    - Method `as_system_label` now returns `SystemLabelId`, removing an unnecessary level of indirection.
- If you *need* a label that is determined at runtime, you can use `Box::leak`. Not recommended.

## Questions for later

* Should we generate a `Debug` impl along with `#[derive(*Label)]`?
* Should we rename `as_str()`?
* Should we remove the extra derives (such as `Hash`) from builtin `*Label` types?
* Should we automatically derive types like `Clone, Copy, PartialEq, Eq`?
* More-ergonomic comparisons between `Label` and `LabelId`.
* Move `Dyn{Eq, Hash,Clone}` somewhere else.
* Some API to make interning dynamic labels easier.
* Optimize string representation
    * Empty string for unit structs -- no debug info but faster comparisons
    * Don't show enum types -- same tradeoffs as asbove.
bors bot pushed a commit that referenced this pull request Jul 14, 2022
# Objective

- Closes #4954 
- Reduce the complexity of the `{System, App, *}Label` APIs.

## Solution

For the sake of brevity I will only refer to `SystemLabel`, but everything applies to all of the other label types as well.

- Add `SystemLabelId`, a lightweight, `copy` struct.
- Convert custom types into `SystemLabelId` using the trait `SystemLabel`.

## Changelog

- String literals implement `SystemLabel` for now, but this should be changed with #4409 .

## Migration Guide

- Any previous use of `Box<dyn SystemLabel>` should be replaced with `SystemLabelId`.
- `AsSystemLabel` trait has been modified.
    - No more output generics.
    - Method `as_system_label` now returns `SystemLabelId`, removing an unnecessary level of indirection.
- If you *need* a label that is determined at runtime, you can use `Box::leak`. Not recommended.

## Questions for later

* Should we generate a `Debug` impl along with `#[derive(*Label)]`?
* Should we rename `as_str()`?
* Should we remove the extra derives (such as `Hash`) from builtin `*Label` types?
* Should we automatically derive types like `Clone, Copy, PartialEq, Eq`?
* More-ergonomic comparisons between `Label` and `LabelId`.
* Move `Dyn{Eq, Hash,Clone}` somewhere else.
* Some API to make interning dynamic labels easier.
* Optimize string representation
    * Empty string for unit structs -- no debug info but faster comparisons
    * Don't show enum types -- same tradeoffs as asbove.
inodentry pushed a commit to IyesGames/bevy that referenced this pull request Aug 8, 2022
# Objective

- Closes bevyengine#4954 
- Reduce the complexity of the `{System, App, *}Label` APIs.

## Solution

For the sake of brevity I will only refer to `SystemLabel`, but everything applies to all of the other label types as well.

- Add `SystemLabelId`, a lightweight, `copy` struct.
- Convert custom types into `SystemLabelId` using the trait `SystemLabel`.

## Changelog

- String literals implement `SystemLabel` for now, but this should be changed with bevyengine#4409 .

## Migration Guide

- Any previous use of `Box<dyn SystemLabel>` should be replaced with `SystemLabelId`.
- `AsSystemLabel` trait has been modified.
    - No more output generics.
    - Method `as_system_label` now returns `SystemLabelId`, removing an unnecessary level of indirection.
- If you *need* a label that is determined at runtime, you can use `Box::leak`. Not recommended.

## Questions for later

* Should we generate a `Debug` impl along with `#[derive(*Label)]`?
* Should we rename `as_str()`?
* Should we remove the extra derives (such as `Hash`) from builtin `*Label` types?
* Should we automatically derive types like `Clone, Copy, PartialEq, Eq`?
* More-ergonomic comparisons between `Label` and `LabelId`.
* Move `Dyn{Eq, Hash,Clone}` somewhere else.
* Some API to make interning dynamic labels easier.
* Optimize string representation
    * Empty string for unit structs -- no debug info but faster comparisons
    * Don't show enum types -- same tradeoffs as asbove.
@alice-i-cecile alice-i-cecile added X-Controversial There is active debate or serious implications around merging this PR and removed X-Controversial There is active debate or serious implications around merging this PR labels Sep 14, 2022
@alice-i-cecile alice-i-cecile marked this pull request as ready for review September 14, 2022 03:16
james7132 pushed a commit to james7132/bevy that referenced this pull request Oct 28, 2022
# Objective

- Closes bevyengine#4954 
- Reduce the complexity of the `{System, App, *}Label` APIs.

## Solution

For the sake of brevity I will only refer to `SystemLabel`, but everything applies to all of the other label types as well.

- Add `SystemLabelId`, a lightweight, `copy` struct.
- Convert custom types into `SystemLabelId` using the trait `SystemLabel`.

## Changelog

- String literals implement `SystemLabel` for now, but this should be changed with bevyengine#4409 .

## Migration Guide

- Any previous use of `Box<dyn SystemLabel>` should be replaced with `SystemLabelId`.
- `AsSystemLabel` trait has been modified.
    - No more output generics.
    - Method `as_system_label` now returns `SystemLabelId`, removing an unnecessary level of indirection.
- If you *need* a label that is determined at runtime, you can use `Box::leak`. Not recommended.

## Questions for later

* Should we generate a `Debug` impl along with `#[derive(*Label)]`?
* Should we rename `as_str()`?
* Should we remove the extra derives (such as `Hash`) from builtin `*Label` types?
* Should we automatically derive types like `Clone, Copy, PartialEq, Eq`?
* More-ergonomic comparisons between `Label` and `LabelId`.
* Move `Dyn{Eq, Hash,Clone}` somewhere else.
* Some API to make interning dynamic labels easier.
* Optimize string representation
    * Empty string for unit structs -- no debug info but faster comparisons
    * Don't show enum types -- same tradeoffs as asbove.
ItsDoot pushed a commit to ItsDoot/bevy that referenced this pull request Feb 1, 2023
# Objective

- Closes bevyengine#4954 
- Reduce the complexity of the `{System, App, *}Label` APIs.

## Solution

For the sake of brevity I will only refer to `SystemLabel`, but everything applies to all of the other label types as well.

- Add `SystemLabelId`, a lightweight, `copy` struct.
- Convert custom types into `SystemLabelId` using the trait `SystemLabel`.

## Changelog

- String literals implement `SystemLabel` for now, but this should be changed with bevyengine#4409 .

## Migration Guide

- Any previous use of `Box<dyn SystemLabel>` should be replaced with `SystemLabelId`.
- `AsSystemLabel` trait has been modified.
    - No more output generics.
    - Method `as_system_label` now returns `SystemLabelId`, removing an unnecessary level of indirection.
- If you *need* a label that is determined at runtime, you can use `Box::leak`. Not recommended.

## Questions for later

* Should we generate a `Debug` impl along with `#[derive(*Label)]`?
* Should we rename `as_str()`?
* Should we remove the extra derives (such as `Hash`) from builtin `*Label` types?
* Should we automatically derive types like `Clone, Copy, PartialEq, Eq`?
* More-ergonomic comparisons between `Label` and `LabelId`.
* Move `Dyn{Eq, Hash,Clone}` somewhere else.
* Some API to make interning dynamic labels easier.
* Optimize string representation
    * Empty string for unit structs -- no debug info but faster comparisons
    * Don't show enum types -- same tradeoffs as asbove.
@hymm
Copy link
Contributor

hymm commented Feb 6, 2023

&str no longer implements label (SystemSet) after #6587

@hymm hymm closed this Feb 6, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-ECS Entities, components, systems, and events C-Breaking-Change A breaking change to Bevy's public API that needs to be noted in a migration guide C-Usability A simple quality-of-life change that makes Bevy easier to use X-Controversial There is active debate or serious implications around merging this PR
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

Remove the ability to directly use strings as labels
3 participants