feather
feather copied to clipboard
HList and Plucker for components
pub trait SendMessage<'a, Index> {
fn send_message(&'a self, message: &str);
}
impl<'a, T, Index> SendMessage<'a, Index> for (&'a Entity, T)
where
T: TupleRef<'a>,
<T as TupleRef<'a>>::HList: PluckerRef<Player, Index>,
{
fn send_message(&'a self, _message: &str) {
let _player: &Player = PluckerRef::<Player, Index>::pluck(&foo);
}
}
for (entity, (player,)) in game.query::<(&Player,)>() {
(&entity, (&player,)).send_message("Hello world!");
(&entity, (&player,)).send_message("Hello world!");
}
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.
My implementation can most likely be made more generic. We can maybe get away with not having TupleRef
and only Tuple
.
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:
error[E0599]: no method named `send_message` found for tuple `(&Entity, &quill::entities::Player)` in the current scope
--> quill/example-plugins/query-entities/src/lib.rs:56:28
|
56 | (&entity, &player).send_message("foo");
| ^^^^^^^^^^^^ method not found in `(&Entity, &quill::entities::Player)`
|
= note: the method `send_message` exists but the following trait bounds were not satisfied:
`&quill::entities::Player: Tuple`
which is required by `(&Entity, &quill::entities::Player): SendMessage<_>`
Also, Feather developers wanting to add new functionality need to figure out the HList
system, which is difficult to understand unless you come from a Haskell background.
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 (&entity, (&component,))
instead
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 query!
macro which generates a QueryResult
struct and implement AsRef
and AsMut
depending on access level.
#[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.
Also mutable references are going to be **** since extracting each field would require a mutable lock reference to the entire T
.
My intentions are not for the user to write
(&entity, (&component,))
insteadlet 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.
The tracking issue for the relevant bindings_after_at
feature for reference: https://github.com/rust-lang/rust/issues/65490
It seems to be close to stabilisation.