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
Trait rework: Variables should not be an associated type, but a marker trait #329
Comments
I think this would massively complicate things. What would Other than |
Hello @mathstuf, I hope you are having a great day. I will try to address your concerns here.
It's important to note that what is really important here is the first (small) code block, and the second one with Now, although this would indeed complicate things a bit as it introduces new traits, I think it would not complicate them too much: it's just a slightly different way to express the notion "
I will try to explain in more detail than I did in my previous message:
I could indeed imagine that one could want to build their query for
As a general conclusion, I would like to underline that these are only a few examples I could think of now, but there could be many more I didn't think of: I beleive this trait system is simply the one that perfectly matches the reality of our concepts (by the fact the concept of trait is the notion of "some property/capability satisfied by some structure", which is precisely what we're trying to represent here, where the property is "once serialized, will be accepted as a valid variables by the graphql server for this query"), making it the correct way to represent them. And once designed, as that is always the case when you're designing a computer system and your representation matches your concepts perfectly, it naturally comes with tons of advantages, among which user-friendliness (generic I hope I was able to answer all of your concerns as well as better explain what I tried to design and the philosophy behind it, and would love to read more feedback. Do you (anybody else welcome of course) have any other question, comment, remark ? |
What enforces that impl<Q> IsValidVariablesFor<Q> for () {
fn as_variables(&self) -> serde_json::Value {
json!({})
}
} |
As a side note which may be useful for easily understanding the rest of this message, Saying For other moral equivalents (typically, where you own the query or the type you're trying to implement it on), a user could specify by themselves that they know that some type is a valid If a user implements that trait themselves, they take responsibility that they're saying "I know this type is valid for that query" (which they can only do if they own the query or the given type). It provides them flexibility and allows again for more user-friendly interfaces. I'm not sure I understand in which scenario a user would mistakenly manually impl |
Replace
That's not how Rust traits work in practice. At least it's not how I'd want traits I'm relying on to ensure that I got my types right to work. It's certainly not an improvement to the status quo in my eyes. I'd find it far more interesting to replace
I saw that. It means that I can't actually use this trait in practice because it doesn't actually guarantee what I'd want. And it wouldn't work anyways because how to I signal that in I find it far more useful for the compiler to tell me "you forgot to add this field you added to your query" than the GraphQL server to tell me after I deploy it. We may as well just take raw JSON for the variables parameter for how much it actually helps me get my types right. Maybe this could be an option for a way to generate code, but I certainly don't think it should be the default. |
Oh, I beleive I found where the misunderstanding is coming from.
As I tried to explain in my first post, the
As a consequence, that's precisely what it still does.
A user could define a type, and declare that they want it to typecheck with any query, but again, people would have to do that on purpose and use that type on purpose, so they wouldn't do it unless they have a legitimate use case for that. So as a prerequisite to doing that they would need that feature. So "somebody does that" implies "this feature is needed for him". (Though I don't know what use case that would be since it kinda defeats the whole point of this library).
I'm afraid I don't see how they work any differently from that. The
I beleive (as per the examples I've pointed out) that's unfortunately the idiomatic way Rust works.
I beleive I've addressed this: it does. But if I'm mistaken and that wasn't the misunderstanding I thought, can you further explain what property you think is not guaranteed unless you specifically write additional code for removing these guarantees ?
As I said, because several different structures could be valid. What's the point of chosing a single one of them for your user, when you could allow him to use any that is valid (while still preventing the ones that are not valid) ? We could for instance automatically generate, through the macro, the owned and non-owned versions of the structures. Or we could generate a single structure, but generic (second code of first post), which would allow for maximum flexibility. Neither of these require you to manually implement |
GraphQLQuery::Variables
is the way currently used to represent that some type is, once serialized, a correctvariables
set for the given query.However we could imagine that several structures, once serialized, would be valid for that query, e.g.
GraphQLQuery::Variables
and&GraphQLQuery::Variables
, or the same structure but with&str
s instead ofString
s.So what we see from that is that fundamentally, "being a valid variable set for query Q once serialized" is a property of some structures, and there may be several of them, so a single associated type is not the proper way to represent them. Instead, this property should be represented using a marker trait.
The same applies to
Response
, with an additional lifetime in the trait (and would help when trying to implement #30). In that case though, we may still want to provide aDefaultResponseData
as an associated type, because it may still be useful when writing generic code running the query and returning deserialized owned data. (fn send_some_query<Q: GraphQLQuery>() -> Q::DefaultResponseData
)Here is what I think the traits should look like:
With this new trait system, we could also generate non-owned versions of the structures using
&str
instead ofString
s and serialize/deserialize from/to them. (#30 🎉), or better yet, replace the current generated structures with generic ones where you could specify the types for each of the fields, and:Obviously this would imply a breaking change but given the current download count this seems reasonable, and better do it now than later! 😉
What do you think ?
The text was updated successfully, but these errors were encountered: