You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Okay, so... facet at its core is all about reflection. With Peek, it's really easy to inspect the type of a value, iterate over its fields, cast it to whatever common types you want, etc. It's a dream of a tool for writing a serializer!
So, how does facet-serialize fit in? The bread and butter is the Serializer trait, which provides 2 things when writing a serializer:
An efficient way to traverse a value as a tree without recursion, using serialize_iterative
A way to view a value as a crushed-down set of common types. Serializers only "see" primitives, and get told when they move "up" or "down" the tree (start_map, start_array, etc.)
(1) is definitely a plus, but isn't without its limitations. For example, facet-json's serializer keeps its own internal stack, separate from the one used by facet-serialize. The Serializer trait alone isn't enough for JSON serialization to be stateless today... which makes me feel like there's something missing from its design?
But (2) is the one I'm more interested in... having a common set of data types to deal with is nice when writing JSON-like serializers, where you have a very minimal data model. But it gives up the flexibility of being able to think about arbitrary data types at the serializer level. A few random thoughts:
YAML supports datetime values as scalars, wouldn't it be better to lean more on reflection for that?
KDL supports arbitrary value tags like (binary)"deadbeefbadc0ffee". What if the facet-kdl crate could have a type like struct TaggedValue<T> { value: T, tag: String }, and could manually pick out that value during serialization? (I don't think that's necessarily the right way to handle tagged values, but the potential to do it this way seems valuable to me)
What's the alternative?
Anyway, that line of thinking started to make me feel that facet-serialize is the wrong layer of abstraction for writing a serializer. Again, facet-serialize (today) basically provides 2 things for serializers, so we would basically need to provide 2 alternatives to kill it:
A function for visiting a Peek value generically as a tree. Actually, this might "just" be a rename of the stuff from facet-serialize... but I do think utilities like stopping traversal and/or choosing which children to visit could be useful too
Convenience methods for translating to common data types. Peek already has a lot of these, but there are some blind spots compared to facet-serialize. e.g., a way to iterate the items of a shape that's either a list or tuple, a way to iterate over key/value pairs of a struct or map, etc.
Sketch: JSON serialization
I sketched out some code effectively overhauling JSON serialization on a branch (permalink). A few notes from that exercise:
JSON serialization is a bit less code than the current implementation (ignoring the iterator impls that should be moved to facet-reflect)
Currently, this version doesn't handle #[facet(flatten)] (causing tests to fail)
It uses a queue during serialization, so recursion / stack overflows aren't a problem, just like the current implementation
It pushes iterators to the queue to serialize arrays/objects, avoiding an extra .collect()
The code still feels pretty flexible to work with. For example, skipping over null values within structs or implementing tagged types both seem like they'd be pretty easy to add on. Plus of course, it keeps the full flexibility of Peek
Initial benchmark results look somewhat promising (seems a tiny bit faster on my machine, but that's probably due to using iterators instead of .collect())
What now?
I wanted to hear other folks chime in about all this! Like I said, I feel that Peek is basically the right layer I'd want for writing a serializer, but there needs to be some more helpers in facet-reflect to make that appealing. If that was a direction that others thought was promising too, I'd be interested in pushing forward on the above branch to get it to a merge-able state (or using this discussion to change its course, etc.)
Or maybe I'm the odd one out! Maybe a simplified data model is the the better option for writing a serializer in most cases, or maybe there's ways to expand Serializer to make it more flexible that I hadn't thought of, etc.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
-
Okay, so... facet at its core is all about reflection. With
Peek
, it's really easy to inspect the type of a value, iterate over its fields, cast it to whatever common types you want, etc. It's a dream of a tool for writing a serializer!So, how does
facet-serialize
fit in? The bread and butter is theSerializer
trait, which provides 2 things when writing a serializer:serialize_iterative
start_map
,start_array
, etc.)(1) is definitely a plus, but isn't without its limitations. For example, facet-json's serializer keeps its own internal stack, separate from the one used by
facet-serialize
. TheSerializer
trait alone isn't enough for JSON serialization to be stateless today... which makes me feel like there's something missing from its design?But (2) is the one I'm more interested in... having a common set of data types to deal with is nice when writing JSON-like serializers, where you have a very minimal data model. But it gives up the flexibility of being able to think about arbitrary data types at the serializer level. A few random thoughts:
(binary)"deadbeefbadc0ffee"
. What if thefacet-kdl
crate could have a type likestruct TaggedValue<T> { value: T, tag: String }
, and could manually pick out that value during serialization? (I don't think that's necessarily the right way to handle tagged values, but the potential to do it this way seems valuable to me)What's the alternative?
Anyway, that line of thinking started to make me feel that
facet-serialize
is the wrong layer of abstraction for writing a serializer. Again,facet-serialize
(today) basically provides 2 things for serializers, so we would basically need to provide 2 alternatives to kill it:Peek
value generically as a tree. Actually, this might "just" be a rename of the stuff fromfacet-serialize
... but I do think utilities like stopping traversal and/or choosing which children to visit could be useful tooPeek
already has a lot of these, but there are some blind spots compared tofacet-serialize
. e.g., a way to iterate the items of a shape that's either a list or tuple, a way to iterate over key/value pairs of a struct or map, etc.Sketch: JSON serialization
I sketched out some code effectively overhauling JSON serialization on a branch (permalink). A few notes from that exercise:
facet-reflect
)#[facet(flatten)]
(causing tests to fail).collect()
Peek
.collect()
)What now?
I wanted to hear other folks chime in about all this! Like I said, I feel that
Peek
is basically the right layer I'd want for writing a serializer, but there needs to be some more helpers infacet-reflect
to make that appealing. If that was a direction that others thought was promising too, I'd be interested in pushing forward on the above branch to get it to a merge-able state (or using this discussion to change its course, etc.)Or maybe I'm the odd one out! Maybe a simplified data model is the the better option for writing a serializer in most cases, or maybe there's ways to expand
Serializer
to make it more flexible that I hadn't thought of, etc.Beta Was this translation helpful? Give feedback.
All reactions