-
Notifications
You must be signed in to change notification settings - Fork 145
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
HList and Plucker for components #388
base: main
Are you sure you want to change the base?
Conversation
My implementation can most likely be made more generic. We can maybe get away with not having |
<T as Tuple>::HList: PluckerRef<Player, Index>, | ||
{ | ||
fn send_message(&self, _message: &str) { | ||
// let _player: &Player = PluckerRef::<Player, Index>::pluck(self.1.hlist()); |
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 not what the lifetimes should be here, since right now Tuple
is only implemented for the owned T
and not &T
.
I'm still concerned about the complexity this introduces for Quill users and developers. It's not intuitive to call methods on tuples, especially when the tuples require trailing commas, and forgetting to insert that comma gives an unhelpful compile error:
Also, Feather developers wanting to add new functionality need to figure out the A solution similar to the current state of this PR can work, but in my opinion, we need better ergonomics and less type-system magic. |
My intentions are not for the user to write let query = world.query::<(Player, Position)>();
for player in query.iter() {
player.send_message("");
}
// Or when it becomes stable
for player @ (entity, (_, Position)) in query.iter() {
player.send_message("");
} Also this is more of an experiment to see what can be done, this is far from final and I'm open for suggestions. |
An alternative approach would be make #[macro_export]
macro_rules! query {
{$(
$ident:ident: $type:ty
),* $(,)?} => {{
struct QueryResult {
$($ident: $type),*
}
$(
impl ::std::convert::AsRef<$type> for QueryResult {
fn as_ref(&self) -> &$type {
&self.$ident
}
}
)*
$(
impl ::std::convert::AsMut<$type> for QueryResult {
fn as_mut(&mut self) -> &mut $type {
&mut self.$ident
}
}
)*
struct Query;
impl Query {
#[allow(dead_code)]
fn iter(&self, game: &mut ::quill::Game) -> impl ::std::iter::Iterator<Item = QueryResult> {
game.query::<($(&$type,)*)>().map(|(_, ($($ident),*))| QueryResult {
$($ident),*
})
}
}
Query
}};
} I kind of like this approach and using it would look something like pub trait SendMessage {
fn send_message(&self, message: &str);
}
impl<T> SendMessage for T
where
T: AsRef<Player>
{
fn send_message(&self, _message: &str) {
...
}
}
let query = query! {
player: Player,
position: Position,
};
for entity in query.iter(game) {
entity.send_message("test");
entity.position.x += 10;
} I can expand this to deal with lifetimes, when support is added. |
The tracking issue for the relevant |
It's still missing support for mutable references and in addition it might also be possible to change the signature a such that
&(Entity, (Player,))
also works.