#Problem trying to write a Generic impl for a Trait

48 messages ยท Page 1 of 1 (latest)

strong shell
#

Hey,

I'm playing around with generics in rust, finding it hard to google my exact question sometimes ๐Ÿค

Would anyone be to let me know if there is a way to make this compile while keeping the associated type on UtilityInput?

pub trait UtilityInput {
    type Q: ReadOnlyWorldQuery;
}

impl<F: Send + Sync + 'static, T: Component> UtilityInput for F
where
    F: Fn(&T) -> f32,
{
    type Q = &'static T;
}

#[derive(Component)]
pub struct AI {}

fn utility_input<In: UtilityInput>(q_ai: Query<&mut AI>, q_input: Query<In::Q>) {}

The error for the impl UtilityInput

the type parameter `T` is not constrained by the impl trait, self type, or predicates
unconstrained type parameter

I want to avoid having to pass multiple generic parameters to the generic utility_input function.

#

for example this works but I now need to duplicate information in the utility_input function?

pub trait UtilityInput<T: ReadOnlyWorldQuery> {}

impl<F: Send + Sync + 'static, T: Component> UtilityInput<&T> for F where F: Fn(&T) -> f32 {}

#[derive(Component)]
pub struct AI {}

fn utility_input<T: ReadOnlyWorldQuery, In: UtilityInput<T>>(
    q_ai: Query<&mut AI>,
    q_input: Query<T>,
) {}

Am I missing something?

somber horizon
somber horizon
#

It's perhaps annoying: This is usually the issue you avoid by replacing a generic (the fn trait arguments are trait-level generics) with an associated type, but you can't do that to Fn

strong shell
#

Thanks for the explanation! I don't really understand what stops rust from deducing <F as UtilityInput>::Q the associated type of UtilityInput from the function signature? I mean all the info is in the function right?

#

But it's not a big deal I was just curious, just means that the later on when I want to write define a concrete utility input function I will have do something like this right:

fn some_func(q: &SomeComponent) -> f32 {1.0}

let input = utility_input<&SomeComponent, some_func>;
somber horizon
#

The issue is that <F as UtilityInput>::Q has two possible answers

#

for the example F I made up earlier

strong shell
#

yeah there's one for every possible T right?

somber horizon
#

In fact, you can construct an F which has infinitely many possible answers, yeah

#
struct F;
//pseudosyntax: The actual appearance of the fn traits is rather WIP
impl<T> Fn(&T) for F {
  type Output = f32;
  fn call(&self, args: &T) {
    0.0
  }
}
somber horizon
strong shell
#

Ok thanks a lot for the help ๐Ÿ™‚

strong shell
#

hmm I've got 90% of the way there, but can't seem to pass my function as a type parameter at the end? Am I missing some magical casting syntax?

pub trait UtilityInput<T: ReadOnlyWorldQuery> {
    fn get_value(&self, query: T::Item<'static>) -> f32;
}

impl<F: Send + Sync + 'static, T: Component> UtilityInput<&T> for F
where
    F: Fn(&T) -> f32,
{
    fn get_value(&self, query: &T) -> f32 {
        let a = query;
        self(a)
    }
}

#[derive(Component)]
pub struct AI {}

fn utility_input<T: ReadOnlyWorldQuery, In: UtilityInput<T>>(
    q_ai: Query<&mut AI>,
    q_input: Query<T>,
    utility_input: In,
) {
    for ai in q_ai.iter() {
        for t in q_input.iter() {
            utility_input.get_value(t);
        }
    }
}

#[cfg(test)]
mod tests {
    use bevy::prelude::App;

    use super::*;

    #[test]
    fn test() {
        let mut app = App::new();
        app.add_system(utility_input::<&SomeData, utility_input_low>);
    }
}

The test won't compile because:

constant provided when a type was expected
`utility_input_low` is a function item, not a type
function item types cannot be named directly
somber horizon
#

They are not types, they're values

#

They have types, but you can't name them. Not directly, at least: there's a trick that might work

strong shell
#

ah that defeats my whole attempt :D, does rust support function factories?

somber horizon
#
fn foo<T>(app: &mut App, _: T) {
  app.add_system(utility_input::<&SomeData, T>)
}

foo(&mut app, utility_input_low)
```This will probably _work_, though it will definitely need more trait bounds than I've given it. 

We can also generalize this and make it a neat method called as, say, `app.add_system_with` or something, but the bigger question is: what are you trying to do?
somber horizon
#

?eval

fn curried_add(x: u32) -> impl Fn(u32) -> u32 {
  move |y| x + y
}

curried_add(1)(2)
warm iceBOT
#
3```
strong shell
#

interesting

#

what are you trying to do?
I've been asked this a lot recently!!

somber horizon
#

I understand the annoyance, but I get the feeling there's a cleaner solution and I can't find it because I don't know the goal ๐Ÿ˜„

strong shell
#

Dw I super appreciate it, I'm learning a lot here, but ultimately trying many different approaches to the same goal because I have no idea what the best approach it, yesterday I thought it was proc macros!

Context: I'm trying to write my own utility AI library to use in a bevy app, so this is supposed to be used as a framework (otherwise it would be pretty trivial).

In the simple example above: I want the user to be able to define input functions, which are basically of the type fn(&Component, ...) -> f32 (ignoring the variadics for now that can be solved with macro implementations I believe), which then creates a 'Bevy system' which is just a function.

However my ultimate goal is to allow a user to specify a whole 'ai system' from reusable parts and make it work with Bevy.

So I have more of a 'desired interface' in my head, and trying to figure out a good implementation that works with that. Something like...

AIBuilder::for(app, AIMarkerComponent)
  .with_decision(Decision::new(SomeDesiredAction, vec![input_one, input_two]))
  .with_decision(Decision::new(SomeOtherAction, vec![input_three, input_four]))
#

but it boils down to allowing users to define and reuse 'input' functions, however there will be an arbitrary number of them. These then collectively define 'decisions' which is a bunch on inputs + some action component.

#

but this is definitely the most difficult thing I've tried in rust so far so a bit out of my depth, although learning a lot ๐Ÿ™‚

#

I think it's because I'm new to thinking about generics in a static language, used to dynamic languages in my day job

somber horizon
#

Hmmm. I think I see what you're doing, but not how this leads to wanting to pass a function as a type

#

(for the record, there is a way to do that with a wrapper function you'd have to call, if I understood correctly, inside the framework, hidden from the user. There might be easier ways, though)

strong shell
#

function as a type was probably just a lack of foresight as to where that attempt was going to end up ๐Ÿ˜„

#

yeah bevy has a lot of nice hidden stuff, but knowing what's exposed and usable is another problem... have been reading their source code a lot

somber horizon
strong shell
#

I think what's really complicating things is the fact that I want to collect the signatures of my input functions and build up the required Query type from those in the outer system

#

I really don't know if that is possible, it feels like it should be, but it's probably hard

#

wait no

#

that was yesterday, I don't need to handle multiple anymore

somber horizon
strong shell
#

ideally, but not married to the idea

#

but user specified logic with user specific inputs yes

somber horizon
#

The required dance here is probably going to be purely traits and associated types: You're effectively doing some type-level programming

The end result will probably look like <FunctionType as HelperTrait>::Output, potentially with large amounts of extra helper traits hidden under this

#

"extracting" the function type from the function value is possible by writing a helper function for it, so this sounds feasible if complex

strong shell
#

alright good to know it's possible, as I said even if it never ends up working I'm learning a lot so will stick with it. Although a bit lost what to try next. so is the first part of the puzzle the helper function to 'extract' the function type from the function?

somber horizon
fickle lotus