#Trait bound without type inference

39 messages · Page 1 of 1 (latest)

steep folio
#

If I understand your problem correctly (which I may not, I'm human), your goal is to check for whether a T satisfies a bound without the compiler performing monomorphization (like through impl<T: Foo> Bar for T).
You have multiple options. Either you only deal with dynamic dispatch (therefore implementing your trait on dyn Foo rather than a monomorphized T), or you deal with type reflection (experimental, see https://github.com/rust-lang/rust/issues/146922).
I don't see why you want to avoid static dispatch (i.e. monomorphization), as sure, it makes the compiler have to determine which types implement a certain trait, but this is usually fast and creates hot paths for implementors to take instead of going through a vtable.

mint forum
# steep folio If I understand your problem correctly (which I may not, I'm human), your goal i...

I am using monomorphization to set variable type after those are declared, and then i use those variables in an operation that requires them to have certain types but here i dont want the variables to automatically get the appropriate types. This is entirely at compile time no static dispatch.

In other words: I define a, b, c with undefined types.
I "set" type for a and b. (using monomorphization)
And then i do an "operation" to get c which requires a and b to be typed in a certain way.

I would want that if the type for b isnt set then the "operation" don't "set" it through monomorphization and instead give a "couldnt resolve type for variable b" error

#

this is the code (after macro expansion)
warning: it is a mess and still wip

steep folio
#

then if you deal with "undefined" types, or rather erasing the types differently at runtime, there's only dynamic dispatch that is gonna solve the issue

mint forum
#

by undefined type i mean which has not been resolved by the type inference yet

#

this is all compile time shenanigans

steep folio
#

I'd appreciate then if you could give an example that does not look like obfuscated code :p
Or you can try to explain what you want to achieve in the first place, why you need to have "unresolved types", and what you mean by "no type inference yet" as type inference only happens at compile time

mint forum
#

i am making a proc macro that parse maths statements and build types and traits at compile time to represent operations you can apply on the statements such as isolating a variable etc

#

basically using types to represent relationships between variables such you can then plug any value into those and the solving code structure already exist at compile time sort of thing

steep folio
#

why not have the traits and solving code structure already present in, say, a crate? What is the requirements for using a proc macro instead?

mint forum
#

because the macro is doing all the simplification logic then figuring out if its linear or quadratic etc... to then produce isolated form for each variable (and other things later)
those are then encoded into their own types which implement traits from the actual crate

#

eg:

pub struct cIsolation<'a, a, c, x> {
            a: &'a a,
            x: &'a x,
            _marker: std::marker::PhantomData<c>,
        }

impl<'a, _T, a, c, x> ComputeIsolate<_T> for cIsolation<'a, a, c, x>
        where
            _T: std::ops::Add<Output = _T> + std::ops::Neg<Output = _T>,
            c: Unknown<_T>,
            a: Constant<_T>,
            x: Constant<_T>,
        {
            fn compute(&self) -> _T {
                (-(-(self.x.get_constant() + self.a.get_constant())))
            }
        }

this encodes how to compute c (using the isolated form)

mint forum
#

anyways this is quite involded and mostly an experiment however getting back to my first message, i am trying to find a trick to get the type inference to work one way (checking if bound is correct) without the other (doing actual monomorphization?)

steep folio
#

I assume that, for example, your generic c changes from implementing Unknown to Constant?
In this case, have you considered using the newtype pattern? like, instead of tagging them with traits, using tuple struct wrappers to easily change their """type""" and therefore what they implement or not

mint forum
steep folio
#
pub struct Unknow<T>(T);
pub struct Constant<T>(T);
// you can then implement specific behavior for Unknow/Constant
// and can easily move T from one to the other
mint forum
#
let c: _ // become Variable0<ConstantState<RealValue>> through set_constant (Constant<T> trait method)
let a: _ // become Variable1<ConstantState<RealValue>> through ComputeIsolate <--- this idealy should not happen
let x: _ // become Variable2<UnknownState<RealValue>> through ComputeIsolate

// SomeVariable<ConstantState<T>> implement Constant<T>
// SomeVariable<UnknownState<T>> implement Unknown<T>
#

this is the current state of things

#

basically i am looking for a way to obscure the relationship between ComputeIsolate and Constant such that the trait can only be used if the variable implement Constant but such that monomorphization to Variable1<UnknownState<RealValue>> doesnt happen (only) in this particular case

mint forum
steep folio
#

This is quite intricate and I'm trying to wrap my head around the issue.
You mean you have some wrappers like VariableN<UnknownState<const RealValue>> but monomorphization somehow considers UnknownState an implementor of Constant?
Forgive me for the amount of questions, it's hard to understand it all without an overview of the problem and the different moving parts that composes it. Do you have a published documentation (docs.rs) somewhere perchance?

mint forum
#

no problem i totally understand that this is quite hard to wrap your head around thanks for still trying to help me
i can give you the code

#
let (c, x, a) = variables!(); // Each variable is a type VariableN<T>, T is unknowned to the compiler at the moment however VariableN<T> implement TypedVariable<T>

pub trait Constant<T> {
    fn set_constant(&self, value: T);
    fn get_constant(&self) -> T;
}

impl<T: TypedVariable<ConstantState<U>>, U: Copy> Constant<U> for T {
    fn set_constant(&self, value: U) {
        self.get_inner().value.set(value);
    }
    fn get_constant(&self) -> U {
        self.get_inner().value.get()
    }
}

c.set_constant(Real(42.)); // Here c becomes Variable0<ConstantState<RealValue>> through monomorphization by the Constant trait

impl<'a, _T, a, c, x> ComputeIsolate<_T> for xIsolation<'a, a, c, x>
        where
            _T: From<f64>
                + std::ops::Sub<Output = _T>
                + std::ops::Div<Output = _T>
                + std::ops::Neg<Output = _T>,
            x: Unknown<_T>,
            a: Constant<_T>,
            c: Constant<_T>,
        {
            fn compute(&self) -> _T {
                ((-(self.c.get_constant() - self.a.get_constant())) / _T::from(-1f64))
            }
        }

stmt.solve_for(&x) // This calls compute from ComputeIsolate which need c and a to be Constant. "c" already implement Constant but not "a", as such type of "a" resolve into Variable1<ConstantState<RealValue>> which i dont want it to do

#

we dont really need to care about the Unknown trait here, my goal would be that each constant has to be defined (using set_constant) in order for ComputeIsolate to be usable and that the ComputeIsolate trait wouldnt define them (to defaults) through monomorphization

#

if you are willing to look into it further

steep folio
#

PS: in your impl ComputeIsolate for xIsolation, you probably should capitalize a, c, and x, so that we can more easily differentiate from the variables named that way, from the generic type associated to such variables, but that's just aesthetics, so feel free to ignore :)

mint forum
mint forum
#

so id think if there is solution to my issue it would probably be in adding a "layer of indirection (type system wise)" between Constant<T> and the actual trait bound for ComputeIsolate to obscure the type inference to the compiler

#

but i don't see a trick using gats or tags, maybe with exotic types such as function pointers or idk

steep folio
#

You say

The compiler want Constant<T> so it tries to make the type implement TypedVariable<ConstantState<T>>
But I'm not completely sure that is what actually happens (again, may be wrong on that).
From how I see the code, the compiler is tasked with resolving the implementation by asking "from the set of all used types within the codebase, which of them satisfy implements TypedVariable of an implementor of ConstantState of a type that is an implementor of Copy", then creates the relevant monomorphized versions of the implementation.

This is where I get confused. If your variables are implementors of TypedVariable<U> where U does not implement ConstantState, then I don't see how it's possible that set_constant be called.

I'll check your source code, not sure I'll get the answer though, we'll see

mint forum
#
impl<T> TypedVariable<T> for VariableN<T> // T could be anything? why do you say does not implement ConstantState?
impl<T: TypedVariable<ConstantState<U>>, U: Copy> Constant<U> for T // U here is refering to the generic on ConstantState which itself also could be anything
steep folio
#

why do you say does not implement ConstantState?
If it does, then the issue lies more with why it is if it's not what you'd expect

mint forum
#

ConstantState is a type, T can be ConstantState<U> which makes VariableN<ConstantState<U>> implement TypedVariable<ConstantState<U>>

#

then any type implementing TypedVariable<ConstantState<U>> for U: Copy implement Constant<U>