Skip to content
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

New protocol and generator options for asynchronous mutating traversal of messages #1762

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

FranzBusch
Copy link
Member

Motivation

Our current Visitor protocol and generated traverse methods allow synchronous throwing traversal of the message fields. This works great for how our current serializers are working; however, often one wants to implement validators or transformers that mutate the fields of message. Sometimes this mutation is even asynchronous.

Modification

This PR adds a few things:

  • A new AsyncVisitor protocol that allows to asynchronously traverse and mutate the fields of a message
  • A code generator option to generate the new async traverse method
  • Tests that validate that mutating async traversal is working

Result

We can now implement async visitors that mutate message fields.

Copy link
Contributor

@Lukasa Lukasa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hrm, I'm really not sure this is a good idea. In general having async traversal of a message type strikes me as being a code smell: can you elaborate on why this is the right solution?

@FranzBusch
Copy link
Member Author

Yeah I can give you a practical example. I have a library that talks with a gRPC service. Many messages to that service contain byte fields. For security purposes those byte fields can be encoded or encrypted. Since those byte fields can be arbitrarily nested it is easier and safer to implement this with a visitor than to hard code it for every single message type.
Now in my particular case the encryption can also be done by a remote service which lead me to making this visitor asynchronous.

Can you elaborate why you would consider this a code smell? I personally think this is a classic "what color is your function problem?" Of course most folks should use the synchronous visitor APIs where possible but it feels overly restrictive to not allow asynchronous visiting.

@FranzBusch FranzBusch added the 🆕 semver/minor Adds new public API. label Mar 10, 2025
@FranzBusch FranzBusch force-pushed the fb-async-traverse branch 4 times, most recently from 62ff381 to 35c49f9 Compare March 10, 2025 13:51
…l of messages

## Motivation

Our current `Visitor` protocol and generated `traverse` methods allow synchronous throwing traversal of the message fields. This works great for how our current serializers are working; however, often one wants to implement validators or transformers that mutate the fields of message. Sometimes this mutation is even asynchronous.

## Modification

This PR adds a few things:
- A new `AsyncVisitor` protocol that allows to asynchronously traverse and mutate the fields of a message
- A code generator option to generate the new async traverse method
- Tests that validate that mutating async traversal is working

## Result

We can now implement async visitors that mutate message fields.
@Lukasa
Copy link
Contributor

Lukasa commented Mar 10, 2025

The sense in which it feels like a code smell is that it has taken a domain modelling problem and pushed it down into the serialization layer, rather than letting the serialization layer handle serialization and having your application use separate data model objects.

One of my pieces of supporting evidence here is that this visitor differs from the other not only in having async methods, but in having those methods be able to modify the structure being visited. That's a pretty substantial divergence from the existing Visitor protocol.

@FranzBusch
Copy link
Member Author

I get your point but I would argue the current visitor pattern is overly focused on just catering for serialization needs whereas a visitor pattern is something generally useful. I agree that if you implement serialization with the visitor pattern this should be both non-mutating and synchronous. However, there are many more useful things to do with visitors such as applying common transformations to a whole message hierarchy.

I don't think we should over index on what the current visitor pattern since that is incredibly tailored to serialization. I tried to implement all of this outside of this library but to properly support it I need code generation and it felt right to me to expand the current visitor pattern with asynchronous and mutating visitors.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🆕 semver/minor Adds new public API.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants